bzoj3992 [SDOI2015]序列统计
2018-02-13 22:30
393 查看
Description
小C有一个集合S,里面的元素都是小于M的非负整数。他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S。小C用这个生成器生成了许多这样的数列。但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个。小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi。另外,小C认为这个问题的答案可能很大,因此他只需要你帮助他求出答案mod 1004535809的值就可以了。
对于10%的数据,1<=N<=1000;
对于30%的数据,3<=M<=100;
对于60%的数据,3<=M<=800;
对于全部的数据,1<=N<=109,3<=M<=8000,M为质数,1<=x<=M-1,输入数据保证集合S中元素不重复
Solution
对于不会做的题可以先从部分分想起,0分暴力也行先考虑dp,有f[i][j]=∑f[i−1][j∗inv(k)%mod](k∈S)f[i][j]=∑f[i−1][j∗inv(k)%mod](k∈S),10分到手
注意到了N的范围进而往矩乘dp想,进而倦生
显然这样会跳进坑里出不来的。引入原根的概念:设原根为g(下同,那么有gimodp≢gjmodp(∀i≠j)gimodp≢gjmodp(∀i≠j)。我们求出m的原根,这样所有数都能表示为gx(x∈[0,m−2])gx(x∈[0,m−2]),两数字相乘就变为指数的加法了
一个数x有原根当且仅当x=2、4、pk、2pkx=2、4、pk、2pk,其中p是奇素数。原根一般比较小可以直接暴力。将x-1质因数分解得到x−1=∏pikix−1=∏piki后,一个数a是x的原根满足∀ax−1pi≢1(modx)∀ax−1pi≢1(modx)而对于x不为质数时x-1换成φ(x)φ(x)即可
那么有f[i][j]=∑f[i−1][j−k]∗c[k]f[i][j]=∑f[i−1][j−k]∗c[k],这里的c[k]c[k]为1当且仅当gk∈Sgk∈S
这就是熟悉的卷积形式了,用NTT是mlogm的。而卷积满足结合律,可以用快速幂加速
NTT与FFT不同之处在于NTT采用原根带入求点值,这里终于写了非递归版的。一个技巧确认能不能用NTT就是看模数有没有原根get√
Code
#include <stdio.h> #include <string.h> #include <vector> #define rep(i,st,ed) for (int i=st;i<=ed;++i) #define fill(x,t) memset(x,t,sizeof(x)) typedef long long LL; const int N=1048586; const int MOD=1004535809; int a ,b ,c ,u ,ans ,rev ; int n,m,x,s,ny,len; int ksm(int x,int dep,int mod) { int ret=1; while (dep) { if (dep&1) ret=(LL)ret*x%mod; dep/=2; x=(LL)x*x%mod; } return ret; } int get_g(int x) { std:: vector <int> v; int tmp=x-1; rep(i,2,x) { if (tmp%i) continue; v.push_back(i); while (tmp%i==0) tmp/=i; } rep(i,2,x) { bool flag=true; rep(j,0,v.size()-1) { if (ksm(i,(x-1)/v[j],x)!=1) continue; flag=false; break; } if (flag) return i; } } void NTT(int *a,int f) { rep(i,0,len-1) if (i<rev[i]) std:: swap(a[i],a[rev[i]]); for (int i=1;i<len;i*=2) { int wn; if (f==1) wn=ksm(3,(MOD-1)/i/2,MOD); else wn=ksm(3,MOD-1-(MOD-1)/i/2,MOD); for (int j=0;j<len;j+=i*2) { int w=1; rep(k,0,i-1) { int u=a[j+k],v=(LL)w*a[j+k+i]%MOD; a[j+k]=(u+v)%MOD; a[j+k+i]=(u-v)%MOD; w=(LL)w*wn%MOD; } } } } void mul(int *c,int *ta,int *tb) { memcpy(a,ta,sizeof(a)); memcpy(b,tb,sizeof(b)); NTT(a,1); NTT(b,1); rep(i,0,len-1) c[i]=(LL)a[i]*b[i]%MOD; NTT(c,-1); rep(i,0,len-1) c[i]=(LL)c[i]*ny%MOD; rep(i,m-1,len-1) c[i-m+1]=(c[i-m+1]+c[i])%MOD,c[i]=0; } void solve(int *c,int dep) { while (dep) { if (dep&1) mul(ans,ans,c); dep/=2; mul(c,c,c); } } int main(void) { scanf("%d%d%d%d",&n,&m,&x,&s); int g=get_g(m),lg=0; for (len=1;len<=m*2;len*=2,lg++); for (int i=0;i<len;i++) rev[i]=(rev[i/2]/2)|((i&1)<<(lg-1)); for (int i=1,w=g;i<m-1;i++,w=(LL)w*g%m) u[w]=i; ny=ksm(len,MOD-2,MOD); rep(i,1,s) { int x; scanf("%d",&x); if (!x) continue; c[u[x]]=1; } ans[0]=1; solve(c,n); printf("%d\n", (ans[u[x]]+MOD)%MOD); return 0; }
相关文章推荐
- [bzoj3992][SDOI2015]序列统计
- BZOJ 3992: [SDOI2015]序列统计 快速幂+NTT(离散对数下)
- [BZOJ3992][SDOI2015]序列统计(DP+原根+NTT)
- bzoj3992【SDOI2015】序列统计
- bzoj3992: [SDOI2015]序列统计
- 【BZOJ】3992 [SDOI2015]序列统计 【离散对数下的NTT】
- 【bzoj3992】 SDOI2015—序列统计
- BZOJ 3992: [SDOI2015]序列统计 NTT+快速幂
- 【BZOJ3992】[SDOI2015]序列统计 NTT+多项式快速幂
- Bzoj3992:[SDOI2015]序列统计
- BZOJ 3992 [SDOI2015]序列统计 NTT
- BZOJ 3992 [SDOI2015]序列统计
- BZOJ 3992: [SDOI2015]序列统计
- BZOJ 3992 Sdoi2015 序列统计 快速数论变换
- 【 bzoj 3992 】 [SDOI2015]序列统计 - NTT 生成函数
- Bzoj3992:[SDOI2015]序列统计
- BZOJ3992 [SDOI2015]序列统计 【生成函数 + 多项式快速幂】
- 【BZOJ】3992: [SDOI2015]序列统计 NTT+生成函数
- 【BZOJ3992】【SDOI2015】序列统计
- Bzoj3992:[SDOI2015]序列统计:NTT+DP