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)

      

      永不放弃。

  • 相关阅读:
    atitit.TokenService v3 qb1 token服务模块的设计 新特性.docx
    Atitit attilax在自然语言处理领域的成果
    Atitit 图像清晰度 模糊度 检测 识别 评价算法 原理
    Atitit (Sketch Filter)素描滤镜的实现  图像处理  attilax总结
    atitit。企业的价值观 员工第一 vs 客户第一.docx
    Atitit 实现java的linq 以及与stream api的比较
    Atitit dsl exer v3 qb3 新特性
    Atititi tesseract使用总结
    Atitit 修改密码的功能流程设计 attilax总结
    atitit.TokenService v3 qb1  token服务模块的设计 新特性.docx
  • 原文地址:https://www.cnblogs.com/LH-Xuanluo/p/11261773.html
Copyright © 2011-2022 走看看