bzoj 4818: [Sdoi2017]序列计数

利用补集转化思想
带素数方案数=无限制方案数-只给合数方案数
用cnt[i]表示1-m中%p=i的数各个数
得到朴素动态规划
$dp[i][j]=\sum_{k=0}^{p-1} dp[i-1][(j-k)%p]*cnt[k]$
复杂度O(np^2)
对于cnt乘法运算,矩乘优化一下
复杂度O(logn*p^2)
/*
 * dp可行方案数-dp没有素数的方案数
 * */
#include<cstdio>
#include<cstring>
#include<algorithm>

#define mod 20170408
inline int  read() {
    int x=0,f=1;
    char c=getchar(); 
    while(c<'0'||c>'9') { if(c=='-')f=-1;c=getchar();} 
    while(c<='9'&&c>='0') x=x*10+c-'0',c=getchar(); 
    return x*f; 
}
#define mod 20170408 
#define LL long long 
int n,m,p,num=0;
struct Matrix { 
    int f[207]; 
    Matrix() {memset(f,0,sizeof f);} 
}a,b,c,d;
Matrix operator * (Matrix &a ,Matrix &b) { 
    Matrix c,d;  
    for(int i=0;i<p;++i)  
        for(int j=0;j<p;++j) { 
            c.f[i+j]=(c.f[i+j]+1LL*a.f[i]*b.f[j])%mod;  
        }
    for(int i=p-1;i>=0;--i) d.f[i]=c.f[i]+c.f[i+p];
    return d;
}
int prime[20000007];bool vis[20000007];
void get_prime() {
    vis[1]=true;
    for(int i=2;i<=m;++i) {
        if(!vis[i]) prime[++num]=i;
        for(int j=1;j<=num&&i*prime[j]<=m;++j) {
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}
int main() { 
    n=read(),m=read(),p=read();
    get_prime();
    for(int i=1;i<=m;++i) {
        a.f[i%p]++; if(vis[i]) b.f[i%p]++;
    }
    for(c.f[0]=d.f[0]=1;n;n>>=1,a=a*a,b=b*b) 
        if(n&1) c=c*a,d=d*b;
    printf("%d\n",(c.f[0]-d.f[0]+mod)%mod);
    return 0;
}

 



posted @ 2018-03-20 21:49  zzzzx  阅读(170)  评论(0编辑  收藏  举报