随 (rand) (dp,原根,矩阵/倍增优化)

随 (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]=\sum\limits f[i-1][j] $(考场上想到的sb DP)

      但$ O(n*m*mod) $的复杂度令人望而却步,并不能多得分。

      于是考虑优化,观察得$mod<=300$而$n<=100000$,想到类似离散化的思路,

      用一个数组$c[i]$记录第$i$数出现的次数

      转移方程式变为:$ f[i][j*a[k]\%mod]=\sum\limits 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]=\sum\limits_{j=1}^{mod-1}\sum\limits_{k=1}^{mod-1}f[i-1][j]*f[i-1][k] $

          $ g[i][j*k\%mod]=\sum\limits_{j=1}^{mod-1}\sum\limits_{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\n",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\n",ans);
67     return 0;
68 }
然后我们就开心的AC了

 

posted @ 2019-07-28 11:29  Toot_Holmes  阅读(297)  评论(0)    收藏  举报