码迷,mamicode.com
首页 > 其他好文 > 详细

SDOI2015]序列统计

时间:2019-01-17 19:39:51      阅读:189      评论:0      收藏:0      [点我收藏+]

标签:取值   sdoi   卷积   ems   乘法   etc   答案   ace   git   

FFT优化背包

可以推出dp式子

乘法不可做。M是质数,变成原根

dp式子现在是加法

其实每次是原来的f数组,对可以转移的s集合进行卷积(即FFT优化背包)

直接快速幂搞定

 

详细一些:

循环卷积无非就是多了一个取值的位置,每次FFT之后,一个位置再变成两个位置的和,剩下>=m的位置再变成0

也有结合律

 

注意,S中有0,而0没有原根,并且x>=1所以选择0一定不是答案。直接pass掉

代码:

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^‘0‘)
#define int long long
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch==-)&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=8005*8;
const int mod=1004535809;
int n,m,o;
int len;
int G;
int s[N],sz;
ll ret[N];
ll qm(ll x,ll y,int mo){
    ll ret=1;
    while(y){
        if(y&1) ret=ret*x%mo;
        x=x*x%mo;
        y>>=1;
    }
    return ret;
}
ll f[N],h[N];
int id[N];
int rev[N];
void fft(ll *f,int n,int c){
    for(reg i=0;i<n;++i){
        if(i<rev[i]) swap(f[i],f[rev[i]]);
    }
    for(reg p=2;p<=n;p<<=1){
        int gen;
        if(c==1) gen=qm(3,(mod-1)/p,mod);
        else gen=qm(qm(3,mod-2,mod),(mod-1)/p,mod);
        int len=p/2;
        for(reg l=0;l<n;l+=p){
            ll buf=1;
            for(reg k=l;k<l+len;++k){
                ll tmp=buf*f[k+len]%mod;
                f[k+len]=(f[k]-tmp+mod)%mod;
                f[k]=(f[k]+tmp)%mod;
                buf=buf*gen%mod;
            }
        }
    }
}
void calc(ll *f,ll *g,int n){
//    cout<<"clac nn "<<n<<endl;
//    cout<<" fff "<<endl;
//    for(reg i=0;i<n;++i){
//        cout<<f[i]<<" ";
//    }cout<<endl;
//    cout<<" ggg "<<endl;
//    for(reg i=0;i<n;++i){
//        cout<<g[i]<<" ";
//    }cout<<endl;
    
    for(reg i=0;i<n;++i) h[i]=g[i];
    fft(f,n,1);fft(h,n,1);
    for(reg i=0;i<n;++i) f[i]=f[i]*h[i]%mod;
    fft(f,n,-1);
    ll inv=qm(n,mod-2,mod);
    for(reg i=0;i<n;++i) f[i]=f[i]*inv%mod,h[i]=f[i];
    for(reg i=1;i<m;++i) {
    //    cout<<f[i]<<" ";
        f[i]=(f[i]+h[i+m-1])%mod;
    }
//    cout<<endl;
    for(reg i=m;i<n;++i) f[i]=0;
}
void findG(){
    for(G=2;;++G){
        bool fl=true;
        for(reg i=1;i<=m-2;++i){
            if(qm(G,i,m)==1) {
                fl=false;break;
            }
        }
        if(fl) break;
    }    
}
void qsm(int y){
    memset(ret,0,sizeof ret);
    ret[0]=1;
    while(y){
        if(y&1) {
            if(ret[0]==1) memcpy(ret,f,sizeof f);
            else calc(ret,f,len);
        }
        calc(f,f,len);
//        /cout<<" ff "<<y<<endl;
//        for(reg i=0;i<n;++i){
//            cout<<f[i]<<" ";
//        }cout<<endl;
        y>>=1;
    }
}
int main(){
    rd(n);rd(m);rd(o);rd(sz);
    int cnt=0;
    int lp;
    for(reg i=1;i<=sz;++i){
        rd(lp);if(lp) s[++cnt]=lp;
    }
    findG();
//    cout<<" GG "<<G<<endl;
    for(reg i=1;i<=m-1;++i){
        id[qm(G,i,m)]=i;
    }
    for(reg i=1;i<=cnt;++i) {
        s[i]=id[s[i]];
        f[s[i]]=1;
    }
    for(lp=m+m,len=1;len<lp;len<<=1);
    for(reg i=0;i<len;++i){
        rev[i]=rev[i>>1]>>1|((i&1)?len>>1:0);
    }
//    cout<<" len "<<len<<endl;
    qsm(n);
    o=id[o];
//    cout<<" o "<<o<<endl;
    printf("%lld",ret[o]);
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2019/1/16 21:29:34
*/

 

SDOI2015]序列统计

标签:取值   sdoi   卷积   ems   乘法   etc   答案   ace   git   

原文地址:https://www.cnblogs.com/Miracevin/p/10283605.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!