随 (rand):
题解:
对于$10\%$的数据mod=2,a[i]必定为1,直接输出1.
对于另$10\%$的数据n=1,输出$ a[1]^m\%mod $ 注意模mod而非$1e9+7$。
对于$50\%$的数据,我们考虑dp,
设$ f[i][j] $为i次操作后结果为j的方案数,
易得转移方程为:$ f[i][j*a[k]\%mod]=sumlimits f[i-1][j] $(考场上想到的sb DP)
但$ O(n*m*mod) $的复杂度令人望而却步,并不能多得分。
于是考虑优化,观察得$mod<=300$而$n<=100000$,想到类似离散化的思路,
用一个数组$c[i]$记录第$i$数出现的次数
转移方程式变为:$ f[i][j*a[k]\%mod]=sumlimits f[i-1][j]*c[k] $
时间复杂度变为$ O(m*mod^2) $,期望得分50pts。
对于$ 100\% $的数据,我们考虑优化,
方法一:矩阵快速幂+原根优化(然而蒟蒻并不会)附上wd大神博客
方法二:倍增优化,
回过头来再看刚才的DP式子,我们可得:$ f[i+q][j*a[k]\%mod]=f[i][j]*f[q][k] $
同样可得:$ f[2*i][j*k\%mod]=f[i][j]*f[i][k] $
于是我们可以在$ O(logm) $的时间求出 $f[2^1],f[2^2],f[2^3]cdots,f[2^n] (2^n<= m< 2^{n+1}) $
所以我们可以使用快速幂的思想,将m二进制拆分,下附普通快速幂模板:
ll qpow(ll x,ll y,ll mod) { ll ans=1; while(y){ if(y&1)ans=ans*x%mod; x=x*x%mod;y>>=1; } return ans; }
将其中y变为m,x变为f数组,ans为g数组。
$f[i][j]$数组含义也变为操作$2^i$次,结果为$j$的方案数。
而$g[i][j]$数组为操作$2^1+2^2+2^3+cdots+2^i$次,结果为$j$的方案数。
所以$ f[i][j*k\%mod]=sumlimits_{j=1}^{mod-1}sumlimits_{k=1}^{mod-1}f[i-1][j]*f[i-1][k] $
$ g[i][j*k\%mod]=sumlimits_{j=1}^{mod-1}sumlimits_{k=1}^{mod-1}g[i-1][j]*f[i][k] $
还有最后一个问题:内存
当前我们的空间复杂度为:$ O(logm*mod) $,而f,g数组都只与上一个状态有关,显然可用滚动数组,最后的空间复杂度为$O(2*mod)$,时间复杂度为$O(mod^2logm)$。

1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 #define R register 8 #define ll long long 9 inline ll read(){ 10 ll aa=0;R int bb=1;char cc=getchar(); 11 while(cc<'0'||cc>'9') 12 {if(cc=='-')bb=-1;cc=getchar();} 13 while(cc>='0'&&cc<='9') 14 {aa=(aa<<1)+(aa<<3)+(cc^48);cc=getchar();} 15 return aa*bb; 16 } 17 const int p=1e9+7; 18 const int N=1005; 19 ll qpow(ll x,ll y,ll mod) 20 { 21 ll ans=1; 22 while(y){ 23 if(y&1)ans=ans*x%mod; 24 x=x*x%mod;y>>=1; 25 } 26 return ans; 27 } 28 ll n,mod,m,tmp=0,cur=0; 29 ll ans,f[2][N],g[2][N]; 30 inline void split(int y) 31 { 32 while(y){ 33 if(y&1){ 34 memset(g[cur^1],0,sizeof(g[cur^1])); 35 for(R int i=1;i<mod;++i) 36 for(R int j=1;j<mod;++j) 37 g[cur^1][i*j%mod]=(g[cur^1][i*j%mod]+f[tmp][i]*g[cur][j]%p)%p; 38 cur^=1; 39 } 40 y>>=1; 41 memset(f[tmp^1],0,sizeof(f[tmp])); 42 for(R int i=1;i<mod;++i){ 43 for(R int j=1;j<mod;++j) 44 f[tmp^1][i*j%mod]=(f[tmp^1][i*j%mod]+f[tmp][i]*f[tmp][j])%p; 45 } 46 tmp^=1; 47 } 48 } 49 int main() 50 { 51 n=read();m=read();mod=read(); 52 if(mod==2){puts("1");return 0;} 53 if(n==1){ 54 int x=read(); 55 ans=qpow(x,m,mod); 56 printf("%lld ",ans%p); 57 return 0; 58 } 59 for(R int i=1;i<=n;++i) 60 ++f[0][read()]; 61 g[0][1]=1; 62 split(m); 63 for(R int i=1;i<=mod;++i) 64 ans=(ans+g[cur][i]*i%p)%p; 65 ans=1ll*ans*qpow(n,m*(p-2),p)%p; 66 printf("%lld ",ans); 67 return 0; 68 }