20190727题解
Posted remarkable
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20190727题解相关的知识,希望对你有一定的参考价值。
T1 随
大神级题目
对于10%的数据mod==2,也就是说所有能选的数全是1,直接输1
另10%的数据n==1,直接快速幂算唯一个数x的m次方即可,注意%mod而不是%1e9+7
对于50%的数据,我们考虑dp
设f[i][j]表示选i次后得到结果为j的方案数
f[i][j*k%mod]=f[i-1][j]*c(k),其中c(k)为待选数中k的个数
因为待选数都<mod,所以时间复杂度为$\Theta(M*mod^2)$
f[i]只与f[i-1]有关,显然可以滚动,空间复杂度$\Theta(mod)$
对于100%的数据,上述方法显然不行
关键在于时间复杂度中的M如何优化
题解:矩阵快速幂!原根!
然而蒟蒻我都不会……
怎么办?
倍增。
我们考虑50%算法的dp转移,显然我们可以得到:
f[i*2][j*k%mod]=f[i][j]*f[i][k]
那么我们可以用$\Theta(log(M))$的时间求出f[2^1],f[2^2],……,f[2^n](2^n<=m<2^(n+1))
但还有一个问题:内存
所以我们考虑利用快速幂的思想,将M二进制拆分,同时用一个g数组记录当前拆到第i位时得数为1,2,3,……,mod-1的方案数
显然g数组会持续更新,我们可以滚动
f[i][j]数组的含义也随之改变为选2^i次待选数为j的方案数,第一维仍然可以滚动
至此,我们已经完美的解决了这个问题。
时间复杂度$\Theta(mod^2log(M))$,空间复杂度$\Theta(mod)$
#include<cstdio> #include<cstring> using namespace std; inline int read(){ int ss=0;char bb=getchar(); while(bb<48||bb>57)bb=getchar(); while(bb>=48&&bb<=57)ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar(); return ss; } int const p=1e9+7,n=read(),m=read(),mod=read(),N=1005; int u=1,v,ug=1,vg; long long f[2][N],g[2][N]; inline int power(long long x,int y,int mo){ long long ans=1; for(;y;y>>=1,x=x*x%mo) if(y&1)ans=ans*x%mo; return ans; } inline void split(int y){ while(y){ if(y&1){ memset(g[ug],0,sizeof(g[ug])); for(register int i=1;i<mod;++i) for(register int j=1;j<mod;++j) g[ug][i*j%mod]=(g[ug][i*j%mod]+f[v][i]*g[vg][j])%p; ug=vg,vg^=1; } y>>=1; memset(f[u],0,sizeof(f[u])); for(register int i=1;i<mod;++i) for(register int j=1;j<mod;++j) f[u][i*j%mod]=(f[u][i*j%mod]+f[v][i]*f[v][j])%p; u=v,v^=1; } return ; } int main(){ if(n==1)return printf("%d",power(read(),m,mod)%p),0; if(mod==2)return putchar(‘1‘),0; for(register int i=1;i<=n;++i) ++f[0][read()]; g[0][1]=1; split(m); long long xs=power(power(n,p-2,p),m,p),ans=0; for(register int i=1;i<mod;++i) ans=(ans+g[vg][i]*i)%p; printf("%lld",ans*xs%p); return 0; }
以上是关于20190727题解的主要内容,如果未能解决你的问题,请参考以下文章