bzoj 4818: [Sdoi2017]序列计数

Description

Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数。Alice还希望
,这n个数中,至少有一个数是质数。Alice想知道,有多少个序列满足她的要求。

Solution

补集转换
用没有质数限制的方案-一个质数都没有的方案

然后这个题是无序的,这样就非常好做了
\(f[i][j]\) 表示选 \(i\) 个数,和 \(\mod p=i\) 的方案数
\(f[i+1][(j+k)\mod P]=f[i][j]*g[k]\)
\(g[k]\) 表示 \(\mod P=k\) 的数的个数

矩阵快速幂优化一下即可

#include<bits/stdc++.h>
using namespace std;
const int N=2e7+10,mod=20170408;
int n,m,P,prime[N],num=0;bool vis[N];
void priwork(){
	vis[1]=1;
    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;
        }
    }
}
struct mat{
    int a[101];
    mat(){memset(a,0,sizeof(a));}
    void clear(){memset(a,0,sizeof(a));}
    mat operator *(const mat &p){
        mat ret;
        for(int i=0;i<P;i++)
            for(int j=0;j<P;j++)
                ret.a[(i+j)%P]=(ret.a[(i+j)%P]+1ll*a[i]*p.a[j])%mod;
        return ret;
    }
};
inline void ksm(mat &S,mat T,int k){
    while(k){
        if(k&1)S=S*T;
        T=T*T;k>>=1;
    }
}
int main(){
  freopen("pp.in","r",stdin);
  freopen("pp.out","w",stdout);
  cin>>n>>m>>P;
  priwork();
  mat S,T;
  S.a[0]=1;
  for(int i=1;i<=m;i++)T.a[i%P]++;
  ksm(S,T,n);
  int ans=S.a[0];
  S.clear();T.clear();S.a[0]=1;
  for(int i=1;i<=m;i++)if(vis[i])T.a[i%P]++;
  ksm(S,T,n);ans=(ans-S.a[0]+mod)%mod;
  printf("%d\n",ans);
  return 0;
}

posted @ 2018-04-28 22:19  PIPIBoss  阅读(166)  评论(0编辑  收藏  举报