BZOJphp
最裸的暴力
設\(f[i][j]\)表示前\(i\)個數,積在膜意義下是\(j\)的方案數
轉移的話,每次枚舉一個數,直接丟進去就好
複雜度\(O(nm|S|)\),10ptsios
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<set> #include<map> #include<vector> #include<queue> using namespace std; #define ll long long #define RG register #define MOD 1004535809 inline int read() { RG int x=0,t=1;RG char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=-1,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*t; } int n,m,X,T; int f[2][10000]; int a[10000]; int main() { n=read();m=read();X=read();T=read(); for(int i=1;i<=T;++i)a[i]=read()%m; for(int i=1;i<=T;++i)f[1][a[i]]++; for(int i=2;i<=n;++i) { for(int j=0;j<m;++j)f[i&1][j]=0; for(int j=1;j<=T;++j) for(int k=0;k<m;++k) (f[i&1][k*a[j]%m]+=f[(i+1)&1][k])%=MOD; } printf("%d\n",f[n&1][X]); return 0; }
發現每一步的轉移是相同的,
所以能夠矩陣快速冪
時間複雜度\(O(lognm^3)\),30pts
我懶得寫了優化
咱們都發現了轉移是相同的
那麼不必定只能用矩陣快速冪呀
咱們的轉移也是知足結合律的
因此能夠把轉移跑快速冪
複雜度\(O(lognm^2)\),60ptsspa
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<set> #include<map> #include<vector> #include<queue> using namespace std; #define ll long long #define RG register #define MOD 1004535809 #define MAX 10000 inline int read() { RG int x=0,t=1;RG char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=-1,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*t; } int n,m,X,T; int f[MAX],s[MAX]; int a[MAX],ret[MAX]; int* zy(int *a,int *b) { memset(ret,0,sizeof(ret)); for(int i=0;i<m;++i) for(int j=0;j<m;++j) (ret[i*j%m]+=1ll*a[i]*b[j]%MOD)%=MOD; return ret; } int main() { n=read();m=read();X=read();T=read(); for(int i=1;i<=T;++i)a[i]=read()%m; for(int i=1;i<=T;++i)f[a[i]]++; bool fl=false; int b=n; while(b) { if(b&1) { if(fl) { memset(ret,0,sizeof(ret)); for(int i=0;i<m;++i) if(s[i]) for(int j=0;j<m;++j) (ret[i*j%m]+=(1ll*s[i]*f[j])%MOD)%=MOD; for(int i=0;i<m;++i)s[i]=ret[i]; } else { for(int i=0;i<m;++i)s[i]=f[i]; fl=true; } } memset(ret,0,sizeof(ret)); for(int i=0;i<m;++i) if(f[i]) for(int j=0;j<m;++j) (ret[i*j%m]+=(1ll*f[i]*f[j])%MOD)%=MOD; for(int i=0;i<m;++i)f[i]=ret[i]; b>>=1; } printf("%d\n",s[X]); return 0; }
如今就是最大的問題了
\(n\)已經優化到了\(logn\)
轉移如今纔是最大的問題
咱們發現轉移是這樣的:
\(f[i]*f[j]\to f[i*j]\)
若是它長成這個樣子:
\(f[i]*f[j]\to f[i+j]\)
這樣子的話就會作啦
這樣就能夠跑一遍多項式的卷積code
怎麼轉換呢?
題目給定的條件\(m\)是質數
咱們知道\(x^{\varphi(m)}\% m=1\)
而若是\(x\)是\(m\)的原根
那麼,對於\(0~\varphi(m)\),
每個原根的若干次冪剛好對應一個數
那麼,這樣的話,乘法能夠轉換成冪的加法get
因而,直接跑多項式的卷積就行了
由於要取膜,只能跑\(NTT\)string
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<set> #include<map> #include<vector> #include<queue> using namespace std; #define ll long long #define RG register #define MOD 1004535809 #define MAX 100000 inline int read() { RG int x=0,t=1;RG char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=-1,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*t; } const int pr=3; const int phi=MOD-1; int n,m,X,T; int f[MAX],s[MAX]; int a[MAX],mp[MAX],b[MAX]; int N,M,l,r[MAX],ret[MAX]; int fpow(int a,int b,int P) { int s=1; while(b){if(b&1)s=1ll*s*a%P;a=1ll*a*a%P;b>>=1;} return s; } int ys[MAX],yst; int getroot(int n) { int tmp=n-1; for(int i=2;i*i<=tmp;++i) if(tmp%i==0) { ys[++yst]=i; while(tmp%i==0)tmp/=i; } if(tmp>1)ys[++yst]=tmp; for(int g=2;g<=n-1;++g) { bool fl=true; for(int i=1;i<=yst;++i) if(fpow(g,(n-1)/ys[i],n)==1){fl=false;break;} if(fl)return g; } return -1; } void getmap() { int prm=getroot(m); for(int i=0;i<m-1;++i) mp[fpow(prm,i,m)]=i; } void preNTT() { M=2*(m-2); for(N=1;N<=M;N<<=1)++l; for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1)); } void NTT(int *P,int opt) { for(int i=0;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]); for(int i=1;i<N;i<<=1) { int W=fpow(pr,phi/(i<<1),MOD); for(int p=i<<1,j=0;j<N;j+=p) { int w=1; for(int k=0;k<i;++k,w=1ll*w*W%MOD) { int X=P[j+k],Y=1ll*P[i+j+k]*w%MOD; P[j+k]=(X+Y)%MOD;P[i+j+k]=(X-Y+MOD)%MOD; } } } if(opt==-1) { reverse(&P[1],&P[N]); int inv=fpow(N,MOD-2,MOD); for(int i=0;i<N;++i)P[i]=1ll*P[i]*inv%MOD; } } void zy(int *a1,int *a2,int *c) { memset(a,0,sizeof(a));memset(b,0,sizeof(b)); for(int i=0;i<m-1;++i)a[i]=a1[i],b[i]=a2[i]; NTT(a,1);NTT(b,1); for(int i=0;i<N;++i)a[i]=1ll*a[i]*b[i]%MOD; NTT(a,-1); memset(ret,0,sizeof(ret)); for(int i=0;i<m-1;++i)ret[i]=(a[i]+a[i+m-1])%MOD; for(int i=0;i<m-1;++i)c[i]=ret[i]; } int main() { n=read();m=read();X=read();T=read(); getmap();preNTT(); for(int i=1;i<=T;++i) { int x=read()%m; if(x)f[mp[x]]++; } s[mp[1]]=1; while(n) { if(n&1)zy(s,f,s); zy(f,f,f); n>>=1; } printf("%d\n",s[mp[X]]); return 0; }