[SDOI2015]序列统计

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
*/

 

转载于:https://www.cnblogs.com/Miracevin/p/10283605.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值