zoukankan      html  css  js  c++  java
  • 训练日志8 (7.27)

    T1 随(rand)

      一道不合常理的出现在T1的巨难题。

      期望$dp$+矩阵乘法+原根优化。

      看到题面的期望,首先可以想到1、2点的特判:

    1. 当$p==2$时,因为对于任何$a_i$都有$0<a_i<p$,所以该情况下我们只需要输出1即可。
    2. 当$n==1$时,我们的选择对象只能是一个数,而我们要选$m$次,因此只需要快速幂输出$a_1^m$即可,这里要注意p与mod的使用

      接下来我们讨论正解思路。

      先思考该题的暴力做法:设$f_{ij}$表示经过$i$次操作后$x$变化为$j$的概率,我们可以想到$dp$式:

          $f_{i j imes a_k\%p}+=f_{i-1 j} imes invn$

      可是这样我们发现复杂度为$O(nmp)$,这显然是不可以接受的,因此我们先要考虑优化$dp$,降低复杂度。

      对于题目中已经给出的特殊性质$a_i$都比较小,且相同的$a_i$对答案的最终贡献是相同的,我们可以想到用来记录某一个相同的$a_i$的个数,于是

          $f_{i j imes k\%p}+=f_{i-1 j} imes b_k imes invn$

    这样我们就可以把复杂度降低到$O(mp^2)$,然后我们就可以开心的$dp$了,并且拿到了50分的好成绩。

      可是尽管$dp$已经很精简(至少我是这么认为,毕竟脑小),但是对于$mleq1e9$的数据范围来说,这仍然不能完美的解决这道美妙的题,$dp$都推到这里了不推了多可惜我们该考虑一下优化$dp$,优化的方向就是把$m$转化成$logm$或直接干掉,观察式子,我们发现这个式子可以转化成矩阵乘法的形式(这个发现很奇妙,我现在还不知道怎么看出来的),那么考虑一下构造矩阵。

      显然在矩阵乘法中第$i$维已经没什么用了。原式中的$f_i$只与能到达它的$f_j$它本身有关,所以在构造矩阵时我们可以依据这个转移状态。

      因为$invn$每次都要乘进去,且需要乘$m$次,且$dp$式是Σ的运算,所以我们可以把$n$直接求其$m$次方的逆元,省去一定时间。

      (下面主要是说给我自己听的,毕竟我不会构造矩阵)

      转移与它自己有关,因此在它自己的对应的位上数值$+1$,也与转移到它的$f_j$有关,且与这些$f_j$的数量也有关,因此在$f_j$对应的位上$+{b_j}$,便的到了新 构建的 可以快速幂的常数矩阵。

      接下来就是快乐的矩阵快速幂取模了。

      快速幂之后只需要将每一位上的得到概率$/%p$求和,再将得到的结果乘上对应的数值,取模(注意取模的对象),复杂度$O(p^3logm)$,就可以快乐的得到80分,(但是$OJ$我只得到了20分,可能是打错了吧)。

      接下来是正解。

      复杂度仍然大到飞起,继续找规律(到这里只能颓题解了)。

      用到了原根,至于这是个什么东西,请大家善用搜索引擎。

      利用原根性质,我们求出了$p$的原根$root$,并且可以用$root$的$n$次方的形式对$p$取模得到小于$p$的任何正整数,我们用次方来来代替上面原数,思路大致不变,就是数的个数转化为这个数是原根的几次方的个数,这时不难发现,原本的乘法转化成了加法,而在构造矩阵的同时,矩阵也由不规则矩阵成为了一个循环矩阵(因为乘变加的原因,构造时类似与对角线的样子),所以我们只需要算出第一行的$m$次方就可以得到整个矩阵的结果,特别注意快速幂基底时定义时$res_0==1$

    这道题就差不多解决了。

      小弟不才。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #define int long long
     5 #define HZOI using namespace std
     6 HZOI;
     7 const int mod=1e9+7;
     8 const int MAXN=1e6+3;
     9 int n,m,p,ans;
    10 int invn,root;
    11 int dp[1003],a[MAXN],b[MAXN],c[MAXN],jz[1003][1003],res[1003],wc[1003];
    12 inline void Work();
    13 inline int Pow(int ,int ,int );
    14 inline void JzPow(int [1003],int );
    15 inline void Cheng(int [1003],int [1003]);
    16 inline void Check(int [1003]);
    17 inline int read();
    18 signed main()
    19 {
    20     n=read(),m=read(),p=read();
    21     Work();
    22     if (p==2) {puts("1");return 0;}
    23     for (int i=1; i<=n; ++i)
    24         a[i]=read(),++b[a[i]];
    25     for (int i=0; i<p; ++i)                        //从0开始
    26         c[i]=b[Pow(root,i,p)];
    27     if (n==1)
    28         {printf("%lld",Pow(a[1],m,p));return 0;}
    29     --p;
    30     invn=Pow(n,mod-2,mod);
    31     for (int i=0; i<p; ++i)
    32         wc[i]=c[i]*invn%mod;
    33     JzPow(wc,m);
    34     for (int i=0; i<p; ++i) ans=(ans+Pow(root,i,p+1)*res[i]+mod)%mod;
    35     printf("%lld
    ",(ans+mod)%mod);
    36 }
    37 inline void JzPow(int a[1003],int M)
    38 {
    39     res[0]=1;                            //不应该出错
    40     while (M)
    41     {
    42         if (M&1) Cheng(res,a);
    43         Cheng(a,a);
    44         M>>=1;
    45     }
    46 }
    47 inline void Cheng(int x[1003],int y[1003])
    48 {
    49     int z[1003];
    50     memset(z,0,sizeof(z));
    51     for (int i=0; i<p; ++i)
    52         for (int j=0; j<p; ++j)
    53             z[(i+j+p)%p]=(z[(i+j+p)%p]+x[i]*y[j]+mod)%mod;
    54     for (int i=0; i<p; ++i)
    55         x[i]=(z[i]+mod)%mod;
    56 }
    57 inline void Work()
    58 {
    59     for (int i=1; i<p; ++i)
    60     {
    61         for (int j=1; j<p; ++j)
    62             if (j!=p-1 and Pow(i,j,p)==1)
    63                 break;
    64             else if (j==p-1 and Pow(i,j,p)==1)
    65                 {root=i;break;}
    66         if (root) break;
    67     }
    68 }
    69 inline int Pow(int x,int y,int M)
    70 {
    71     int ans=1;
    72     while (y)
    73     {
    74         if (y&1) ans=ans*x%M;
    75         x=x*x%M;
    76         y>>=1;
    77     }
    78     return (ans+M)%M;
    79 }
    80 inline int read()
    81 {
    82     int res=0; char ch=getchar();
    83     while (ch<'0' or ch>'9') ch=getchar();
    84     while (ch>='0' and ch<='9') res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    85     return res;
    86 }
    随(rand)

    T2 单(single)

      这道题考试的时候没仔细想,也没有搞测试点,蒙到了10分。

      考得思想,前缀和后缀和思想,代换思想。

      先说$O(n)$求$b_i$的做法。

      维护某一个点的前缀和后缀,在转移时就会有它某一方向的所有$a_i$$-1$,另一方向所有$a_i$$+1$,这就是前缀后缀的用处了。

      维护$b$数组一个道理只是需要多次转换化简。

      注意在树上统计前后缀时,通过移项出的式子进行更新答案,树的总$a_i$值很重要。

      具体见代码吧,写博客的时间比较晚了。

      小弟不才。

      

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<cmath>
      4 #include<iostream>
      5 #define int long long
      6 #define HZOI using namespace std
      7 HZOI;
      8 const int MAXN=1e5+3;
      9 int T,n,opt;
     10 int tt,first[MAXN],vv[MAXN<<2],nx[MAXN<<2];
     11 int a[MAXN],b[MAXN],sc[MAXN];
     12 inline void Add(int ,int );
     13 inline int read();
     14 int Dfs00(int ,int );
     15 void Dfs01(int ,int );
     16 void Dfs10(int ,int );
     17 int Dfs11(int ,int );
     18 signed main()
     19 {
     20     T=read();
     21     while (T--)
     22     {
     23         tt=0,memset(first,0,sizeof(first));
     24         memset(a,0,sizeof(a));
     25         memset(b,0,sizeof(b));
     26         memset(sc,0,sizeof(sc));
     27         n=read();
     28         for (int i=1,x,y; i<n; ++i)
     29         {
     30             x=read(); y=read();
     31             Add(x,y); Add(y,x);
     32         }
     33         opt=read();
     34         if (opt==0)
     35         {
     36             for (int i=1; i<=n; ++i) a[i]=read();
     37             sc[1]=Dfs00(1,0);
     38             Dfs01(1,0);
     39             for (int i=1; i<=n; ++i) printf("%lld ",b[i]);
     40             puts("");
     41         }
     42         else 
     43         {
     44             for (int i=1; i<=n; ++i) b[i]=read();
     45             Dfs10(1,0);
     46             sc[1]=(sc[1]+2*b[1])/(n-1);
     47             a[1]=sc[1];
     48             Dfs11(1,0);
     49             for (int i=1; i<=n; ++i) printf("%lld ",a[i]);
     50             puts("");
     51         }
     52     }
     53 }
     54 int Dfs11(int k,int father)
     55 {
     56     for (int i=first[k]; i; i=nx[i])
     57         if (vv[i]!=father)
     58         {
     59             a[vv[i]]+=(b[k]-b[vv[i]]+sc[1]);
     60             if (k^1) a[k]-=(b[k]-b[vv[i]]+sc[1]);
     61             if (k^1) sc[k]+=Dfs11(vv[i],k);
     62             else a[k]-=Dfs11(vv[i],k);
     63         }
     64     if (k^1) {a[k]>>=1; sc[k]+=a[k];}
     65     return sc[k];
     66 }
     67 void Dfs10(int k,int father)
     68 {
     69     for (int i=first[k]; i; i=nx[i])
     70         if (vv[i]!=father)
     71         {
     72             sc[1]+=b[vv[i]]-b[k];
     73             Dfs10(vv[i],k);
     74         }
     75 }
     76 void Dfs01(int k,int father)
     77 {
     78     for (int i=first[k]; i; i=nx[i])
     79         if (vv[i]!=father)
     80         {
     81             b[vv[i]]=b[k]-2*sc[vv[i]]+sc[1];   
     82             Dfs01(vv[i],k);
     83         }
     84 }
     85 int Dfs00(int k,int father)
     86 {
     87     sc[k]=a[k];
     88     for (int i=first[k]; i; i=nx[i])
     89         if (vv[i]!=father)
     90             sc[k]+=Dfs00(vv[i],k);
     91     if (k^1) b[1]+=sc[k];
     92     return sc[k];
     93 }
     94 inline void Add(int u,int v)
     95 {
     96     vv[++tt]=v; nx[tt]=first[u]; first[u]=tt;
     97 }
     98 inline int read()
     99 {
    100     int res=0; char ch=getchar();
    101     while (ch<'0' or ch>'9') ch=getchar();
    102     while (ch>='0' and ch<='9') res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    103     return res;
    104 }
    单(single)

     

    T3 题(problem)

      几乎原题。35分。

      情况为0和1的时候都是原题,但由于取模没有好好取,导致无缘无故少了15分,很难受。

      其实情况为3也很好想,其实就是比1稍微难一点,两个$Catalan$相乘,再乘上组合数,就解决了。

      其实2情况是没想到的,需要用到一个$dp_i$,定义为走了$i$步回到原点的方案数,知道了$dp$,公式也就不难想了,四个方向,每一个方向都是一个$Catalan$,乘起来就可以了,只不过这里需要注意,这样写可能会有重复情况,需要稍微更改一下$Catalan$的公式,结果为

             $dp_i=sum limits_{j=0}^{i-1}4 dp_j C_{i-j-2}^{frac{i-j-2}{2}} C_{i-j-2}^{frac{i-j-4}{2}}$

      式子可能确实有些奇怪,不过仔细想想,它确实排除了不同的$i$有相同效果的影响。

      小弟不才。

      

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstdlib>
     4 #define int long long 
     5 #define HZOI using namespace std
     6 HZOI;
     7 const int MAXN=1e6+3;
     8 const int mod=1e9+7;
     9 int n,typ;
    10 int inv[MAXN],F[MAXN],Finv[MAXN];
    11 int dp[MAXN];
    12 inline void Init();
    13 inline int Comb(int ,int );
    14 signed main()
    15 {
    16     Init();
    17     scanf("%lld%lld",&n,&typ);
    18     if (!typ)
    19     {
    20         int ans=0;
    21         for (int a=0; a<=n/2; ++a)
    22             ans=(Comb(n,a)%mod*Comb(n-a,a)%mod*Comb(n-2*a,(n/2)-a)%mod+ans+mod)%mod;
    23         printf("%lld",(ans+mod)%mod);
    24     }
    25     else if (typ==1)
    26         printf("%lld",((Comb(n,n/2)-Comb(n,(n/2)-1))+mod)%mod);
    27     else if (typ==2)
    28     {
    29         dp[0]=1;
    30         for (int i=2; i<=n; i+=2)
    31         {
    32             for (int j=0; j<i; j+=2)
    33                 dp[i]=(dp[i]+4*dp[j]%mod*(Comb((i-j-2),(i-j)/2-1)-Comb(i-j-2,(i-j)/2-2))%mod+mod)%mod;
    34         }
    35         printf("%lld",(dp[n]+mod)%mod);
    36     }
    37     else 
    38     {
    39         int ans=0;
    40         for (int a=0; a<=n/2; ++a)
    41             ans=(ans+Comb(n,a*2)%mod*(Comb(2*a,a)-Comb(2*a,a-1))%mod*(Comb(n-2*a,(n/2)-a)-Comb(n-2*a,(n/2)-a-1))%mod+mod)%mod;
    42         printf("%lld",(ans+mod)%mod);
    43     }
    44 }
    45 inline int Comb(int x,int y)
    46 {
    47     return F[x]%mod*Finv[y]%mod*Finv[x-y]%mod;
    48 }
    49 inline void Init()
    50 {
    51     inv[1]=1;
    52     for (int i=2; i<=100000; ++i) 
    53         inv[i]=(mod-mod/i)*1ll*inv[mod%i]%mod;
    54     Finv[0]=F[0]=1;
    55     for (int i=1; i<=100000; ++i)
    56         F[i]=F[i-1]%mod*1ll*i%mod,
    57         Finv[i]=Finv[i-1]%mod*1ll*inv[i]%mod;
    58 }
    题(problem)

      

      永不放弃。

  • 相关阅读:
    理解RabbitMQ中的AMQP-0-9-1模型
    深入分析Java反射(八)-优化反射调用性能
    一张图帮你记忆,Spring Boot 应用在启动阶段执行代码的几种方式
    Java equals 和 hashCode 的这几个问题可以说明白吗?
    如何妙用Spring 数据绑定机制?
    Lombok 使用详解,简化Java编程
    Java升级那么快,多个版本如何灵活切换和管理?
    手把手教你定制标准Spring Boot starter,真的很清晰
    Java12 Collectors.teeing 你需要了解一下
    Maven optional关键字透彻图解
  • 原文地址:https://www.cnblogs.com/LH-Xuanluo/p/11261773.html
Copyright © 2011-2022 走看看