1. 程式人生 > >Shoi2017(六省聯考)Day1題解

Shoi2017(六省聯考)Day1題解

終於發現自己是真殘了(我這句話都說了幾遍了QAQ)

T1:容易發現答案只與最後一科出成績的時間有關,列舉這個時間,然後按順序維護代價就行了。
BZ上的資料範圍不對,不過影響不大,但是有一個特別坑比的點是C=1016,為了這個SB點卡了倆小時氣都氣死了

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
using namespace std;

int A,B;
long long C;
int n,m;
int a[M],b[M];
long
long ans=0x3f3f3f3f3f3f3f3fll; int main() { cin>>A>>B>>C; cin>>n>>m; for(int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+n+1); for(int i=1;i<=m;i++) scanf("%d",&b[i]); sort(b+1,b+m+1); int p=1,q=1; long long
t=0; long long l=0,r=0; for(int i=1;i<=m;i++) r+=b[i]; for(int i=1;i<=(C==10000000000000000ll?a[1]:b[m]);i++) { long long re=0; while(p<=n && a[p]<i) ++p; t+=p-1; re+=t*C; while(q<=m && b[q]<i) ++q; r-=m-q+1
; l+=q-1; if(B<A) re+=B*r; else re+=A*min(l,r)+B*max(r-l,0ll); ans=min(ans,re); #ifndef ONLINE_JUDGE cout<<t<<' '<<l<<' '<<r<<' '<<re<<endl; #endif } cout<<ans<<endl; return 0; }

T2:
這……
這……
這不是上帝與集合的……
正確用法……
嗎??

需要用到這樣一個引理:
aφ(p)時,xaxamodφ(p)+φ(p)(modp)
這個引理不需要(a,p)=1就能用!

然後就簡單了,因為p最多φlog次就會變成1,所以根據這個引理可以看出來,一個數最多進行log次操作就不動了
預處理每個數進行log次操作後的結果,利用線段樹維護區間和,以及區間內是否都不動了,就行了
時間複雜度O(nlog3p+mlognlogp)
為了卡常數我LOG只開了15實際上至少要開26以上才能保證正確……所以別HACK我程式碼拜託

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 50500
#define LOG 15
using namespace std;

int n,m,p,c;
int a[M];
int phi[LOG];
int next[M][LOG],now[M];

struct Segment_Tree
{
    Segment_Tree *ls,*rs;
    int sum;
    bool flag;
    friend void* operator new(size_t)
    {
        static Segment_Tree mempool[M<<1],*C=mempool;
        return C++;
    }
    void Update()
    {
        sum=(ls->sum+rs->sum)%p;
        flag=ls->flag&rs->flag;
    }
    void Build_Tree(int l,int r)
    {
        int mid=(l+r)>>1;
        if(l==r)
        {
            sum=a[mid];
            return ;
        }
        (ls=new Segment_Tree)->Build_Tree(l,mid);
        (rs=new Segment_Tree)->Build_Tree(mid+1,r);
        Update();
    }
    void Modify(int l,int r,int x,int y)
    {
        //printf("Modify:%d %d %d %d\n",l,r,x,y);
        int mid=(l+r)>>1;
        if(flag) return;
        if(l==r)
        {
            sum=next[mid][++now[mid]];
            if(now[mid]==LOG-1)
                flag=true;
            return ;
        }
        if(x==l&&y==r)
        {
            ls->Modify(l,mid,x,mid);
            rs->Modify(mid+1,r,mid+1,y);
            Update();
            return ;
        }
        if(y<=mid)
            ls->Modify(l,mid,x,y);
        else if(x>mid)
            rs->Modify(mid+1,r,x,y);
        else
            ls->Modify(l,mid,x,mid),rs->Modify(mid+1,r,mid+1,y);
        Update();
    }
    int Query(int l,int r,int x,int y)
    {
        int mid=(l+r)>>1;
        if(x==l&&y==r)
            return sum;
        if(y<=mid)
            return ls->Query(l,mid,x,y);
        if(x>mid)
            return rs->Query(mid+1,r,x,y);
        return (ls->Query(l,mid,x,mid)+rs->Query(mid+1,r,mid+1,y))%p;
    }
}*root=new Segment_Tree;

int Phi(int n)
{
    int re=n;
    for(int i=2;i*i<=n;i++)
        if(n%i==0)
        {
            re/=i;
            re*=i-1;
            while(n%i==0)
                n/=i;
        }
    if(n^1) re/=n,re*=n-1;
    return re;
}

int Fake_Quick_Power(long long x,int y)
{
    long long re=1;
    if(y>p) return p+1;
    while(y)
    {
        if(y&1)
        {
            re*=x;
            if(re>p)
                return p+1;
        }
        x*=x;
        if(x>p)
            return p+1;
        y>>=1;
    }
    return re;
}

int Quick_Power(long long x,int y,int mod)
{
    long long re=1;
    while(y)
    {
        if(y&1) (re*=x)%=mod;
        (x*=x)%=mod; y>>=1;
    }
    return re;
}

int Calculate(int c_cnt,int &mod,int &power)
{
    if(mod==1)
        return 0;
    if(power<p)
        return power%mod;
    //if(*(&power-1)<*(&mod+1))
    //  return Quick_Power(c,*(&power-1),mod);
    return Quick_Power(c,Calculate(c_cnt-1,*(&mod+1),*(&power-1))+*(&mod+1),mod);
}

int main()
{
    cin>>n>>m>>p>>c;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    phi[0]=p;
    for(int i=1;i<LOG;i++)
        phi[i]=Phi(phi[i-1]);
    for(int i=1;i<=n;i++)
    {
        static int power[LOG];
        power[0]=a[i];
        for(int j=1;j<LOG;j++)
            power[j]=Fake_Quick_Power(c,power[j-1]);
        for(int j=1;j<LOG;j++)
            next[i][j]=Calculate(j,phi[0],power[j]);
    }


    root->Build_Tree(1,n);
    for(int i=1,type,l,r;i<=m;i++)
    {
        scanf("%d%d%d",&type,&l,&r);
        if(type==0)
            root->Modify(1,n,l,r);
        else
            printf("%d\n",root->Query(1,n,l,r));
    }
    return 0;
}

T3:看那個組合數的直觀含義就是nk個球裡面取m個,m%k=r的方案數
f[i][j]表示i個球裡面取m個,m%k=j的方案數,則f[i][j]=f[i1][j]+f[i1][(j1)%k]
矩陣乘法加速轉移,沒了……

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 55
using namespace std;

int n,p,k,r;

struct Matrix
{
    int a[M][M];
    Matrix(bool flag=false)
    {
        memset(a,0,sizeof a);
        for(int i=0;i<k;i++)
            a[i][i]=flag;
    }
    int* operator [] (int i)
    {
        return a[i];
    }
    const int* operator [] (int i) const
    {
        return a[i];
    }
    friend Matrix operator * (const Matrix &x,const Matrix &y)
    {
        Matrix z(0);
        for(int i=0;i<k;i++)
            for(int j=0;j<k;j++)
                for(int kk=0;kk<k;kk++)
                    (z[i][j]+=(long long)x[i][kk]*y[kk][j]%p)%=p;
        return z;
    }
    friend Matrix Quick_Power(Matrix x,long long y)
    {
        Matrix re(1);
        while(y)
        {
            if(y&1) re=re*x;
            x=x*x; y>>=1;
        }
        return re;
    }
};

int main()
{
    cin>>n>>p>>k>>r;
    Matrix x(false);
    for(int i=0;i<k;i++)
        x[i][i]++,x[(i+k-1)%k][i]++;
    x=Quick_Power(x,(long long)n*k);
    cout<<x[0][r]<<endl;
    return 0;
}