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;
}
View Code

 

以上是关于20190727题解的主要内容,如果未能解决你的问题,请参考以下文章

20190727-只是睡着了

Java 求解划分字母区间

集训日记

微信小程序代码片段

VSCode自定义代码片段——CSS选择器

谷歌浏览器调试jsp 引入代码片段,如何调试代码片段中的js