zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",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
    ",ans%p);
    83     }
    84 }
    这里放出暴力dp和矩阵优化dp

    第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
    ",ans);
    59 }
    完整代码

    更加优越的算法(???):本质上我们要做的是循环卷积,可以使用fft.但本题的模数使得fft较为不方便..

     

     

  • 相关阅读:
    Android Studio 1.0 初体验
    JAVA笔记:死锁的详细解释
    JAVA笔记:多线程的理解及应用(三)
    JAVA笔记:多线程的理解及应用(二)
    JAVA笔记:多线程的理解及应用(一)
    mysql 中文编码
    k8s删除node
    k8s 卸载
    kubernetes 集群master变更ip地址
    docker常用指令
  • 原文地址:https://www.cnblogs.com/Al-Ca/p/11257656.html
Copyright © 2011-2022 走看看