HZOJ 随
这个题的题解并不想写……一个写的很详细的blog
第1个测试点:mod=2,a[i]<mod(仔细看题),则n个数字都是1,直接输出1即可.
第2个测试点:每次乘上去的数字只有一种选择,快速幂即可.
第3,4,5个测试点:定义f[i][j]表示i次操作后x的数值为j的概率.直接转移,复杂度O(m*mod^2)
1 f[0][1]=1; 2 for(int i=0;i<m;i++) 3 for(int j=0;j<mod;j++) 4 for(int k=0;k<mod;k++)//这里不要枚举n啊,可以先处理出来每个数出现的概率 5 f[i+1][j*k%mod]=(f[i+1][j*k%mod]+f[i][j]*v[k]%p)%p;
第6,7,8个测试点:第3,4,5个测试点中的DP转移可以转化为矩阵乘法形式,利用矩阵快速幂进行优化,复杂度O(mod^3*logm)矩阵快速幂优化dp见--->这里
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #define int LL 6 #define MAXN 100010 7 #define LL long long 8 using namespace std; 9 struct jz 10 { 11 LL m[310][310]; 12 void clear(){memset(m,0,sizeof(m));} 13 }base; 14 int n,m,mod,a[MAXN]; 15 const int p=1e9+7; 16 jz operator * (jz &a,jz &b) 17 { 18 jz ans;ans.clear(); 19 for(int i=0;i<mod;i++) 20 for(int j=0;j<mod;j++) 21 for(int k=0;k<mod;k++) 22 ans.m[i][j]=(ans.m[i][j]+a.m[i][k]*b.m[k][j]); 23 for(int i=0;i<mod;i++) 24 for(int j=0;j<mod;j++) 25 ans.m[i][j]%=p; 26 return ans; 27 } 28 jz operator ^ (jz a,int b) 29 { 30 jz ans=a;b--; 31 while(b) 32 { 33 if(b&1)ans=ans*a; 34 a=a*a; 35 b=b>>1; 36 } 37 return ans; 38 } 39 LL f[1010][310],tem; 40 LL v[310]; 41 LL poww(LL a,int b,int mod) 42 { 43 LL ans=1; 44 while(b) 45 { 46 if(b&1)ans=ans*a%mod; 47 a=a*a%mod; 48 b=b>>1; 49 } 50 return ans; 51 } 52 signed main() 53 { 54 // freopen("in.txt","r",stdin); 55 // freopen("0.out","w",stdout); 56 57 cin>>n>>m>>mod;tem=poww(n,p-2,p); 58 for(int i=1;i<=n;i++)cin>>a[i],v[a[i]%mod]=(v[a[i]%mod]+tem)%p; 59 if(mod==2){puts("1");return 0;} 60 if(n==1){printf("%lld\n",poww(a[1],m,mod));return 0;} 61 if(m<=1000) 62 { 63 f[0][1]=1; 64 for(int i=0;i<m;i++) 65 for(int j=0;j<mod;j++) 66 for(int k=0;k<mod;k++) 67 f[i+1][j*k%mod]=(f[i+1][j*k%mod]+f[i][j]*v[k]%p)%p; 68 LL ans=0; 69 for(int i=0;i<mod;i++) 70 ans=(ans+f[m][i]*i%p)%p; 71 printf("%lld\n",ans%p); 72 } 73 else if(mod<=300) 74 { 75 for(int j=0;j<mod;j++) 76 for(int k=0;k<mod;k++) 77 base.m[j*k%mod][j]=(base.m[j*k%mod][j]+v[k])%p; 78 base=base^m; 79 LL ans=0; 80 for(int i=0;i<mod;i++) 81 ans=(ans+base.m[i][1]*i%p)%p; 82 printf("%lld\n",ans%p); 83 } 84 }
第9,10个测试点(标算):上面那个blog讲的非常详细了(然而我并没有看懂……),总感觉这个矩阵优化dp怪怪的……
先把达哥题解放出来:
利用原根进行转化,则乘法转化为加法,f[i][j]表示i次操作后x取模后等于原根的j次方的概率.指数需要对(mod-1)取模.这样转化一下我们发现转移还是矩阵的形式,而且是循环矩阵的形式.循环矩阵快速幂,复杂度O(mod^2*logm)
然后是另一种标算(本人写的这个):
定义f[i][j]表示i次操作后变成原根的j次方的概率.求出p[i][j]表示2^i次操作后变成原根的j次方的概率.倍增的思想求出f[m][]这个数组.也是O(mod^2*logm)
1 for(int i=0;i<=mod-2;i++)p[0][i]=tem*cnt[i]%mp;//乘2^0为rt^i的概率,tem为分母的逆元,cnt为个数。 2 for(int i=1;i<=32;i++) 3 for(int j=0;j<=mod-2;j++) 4 for(int k=0;k<=mod-2;k++) 5 p[i][(j+k)%(mod-1)]=(p[i][(j+k)%(mod-1)]+p[i-1][j]*p[i-1][k]%mp)%mp;
然后用类似快速幂的方法就可以求出了,避免了直接理解矩阵。
1 #include<iostream> 2 #include<cstdio> 3 #define MAXN 400010 4 #define LL long long 5 #define int LL 6 using namespace std; 7 const int mp=1e9+7; 8 int n,m,mod,a[MAXN]; 9 int xp[3100],cnt[3100]; 10 int rt; 11 int p[34][1005],f[2][1005]; 12 LL poww(LL a,int b,int mod) 13 { 14 LL ans=1; 15 while(b) 16 { 17 if(b&1)ans=ans*a%mod; 18 a=a*a%mod; 19 b=b>>1; 20 } 21 return ans; 22 } 23 void findroot() 24 { 25 for(rt=2;rt<mod;rt++) 26 { 27 bool pd=0; 28 for(int i=1;i<mod-2;i++) 29 if(poww(rt,i,mod)==1){pd=1;break;} 30 if(pd||poww(rt,mod-1,mod)!=1)continue; 31 else break; 32 } 33 } 34 signed main() 35 { 36 cin>>n>>m>>mod; 37 findroot();int tem=poww(n,mp-2,mp),x; 38 xp[0]=1;for(int i=1;i<=mod;i++)xp[i]=xp[i-1]*rt%mod; 39 for(int i=0;i<=mod-2;i++)a[xp[i]]=i; 40 for(int i=1;i<=n;i++)cin>>x,cnt[a[x]]++; 41 for(int i=0;i<=mod-2;i++)p[0][i]=tem*cnt[i]%mp;//乘2^0为rt^i的概率; 42 for(int i=1;i<=32;i++) 43 for(int j=0;j<=mod-2;j++) 44 for(int k=0;k<=mod-2;k++) 45 p[i][(j+k)%(mod-1)]=(p[i][(j+k)%(mod-1)]+p[i-1][j]*p[i-1][k]%mp)%mp; 46 f[0][0]=1; 47 for(int k=0;m;m/=2,k++) 48 if(m&1) 49 { 50 for(int i=0;i<=mod-2;i++)f[1][i]=0; 51 for(int i=0;i<=mod-2;i++) 52 for(int j=0;j<=mod-2;j++) 53 f[1][(j+i)%(mod-1)]=(f[1][(j+i)%(mod-1)]+f[0][i]*p[k][j]%mp)%mp; 54 for(int i=0;i<=mod-2;i++)f[0][i]=f[1][i]; 55 } 56 LL ans=0; 57 for(int i=0;i<=mod-2;i++)ans=(ans+f[0][i]*xp[i]%mp)%mp; 58 printf("%lld\n",ans); 59 }
更加优越的算法(???):本质上我们要做的是循环卷积,可以使用fft.但本题的模数使得fft较为不方便..
波澜前,面不惊。