zoukankan      html  css  js  c++  java
  • noip模拟测试9


    T1:给出n个正整数a1,a2…an和一个质数mod.一个变量x初始为1.

      进行m次操作.每次在n个数中随机选一个ai,然后x=xai.问m次操作之后x的取值的期望.

      (1<=ai<mod    mod为质数    1<=mod<=1000    1<=n<=105 , 1<=m<=109

      第一眼康上去感觉是个数论加期望,完全不想做,想想后发现是个假期望,但好像是真数论,还是不想做

      于是直接跳过,最后也没什么思路,打了个n×m×mod的暴力,然后喜暴 0 

      考完后,听大佬们分享,才想到这是个矩阵优化dp

      

      那说说思路:

      首先会发现mod极小,而m极大,那么最基本的思路就是用mod复杂度增加的代价将m的复杂度降低

      猜测m复杂度为log级别,那么就想log级的算法

      想到如果设计一个状态 f [ i ][ j ] 表示操作 i 此后变成 j 的期望

      那么每次转移的系数矩阵都是相同的!

      我们就可以用矩阵快速幂来优化dp的转移,于是复杂度为:O ( mod3 log(m) )

      

      可是还是过不了,思路有问题吗?显然没有,那怎么优化呢?

      关于矩乘的优化,想到循环矩阵的优化,于是看能不能转变系数矩阵的定义,让其成为循环矩阵

      发现题目一个很妙的性质,1<=ai<mod,于是想到用原根优化(???)

      设原根为 rt ,若 i = xp[i] 那么,我们最开始的式子是 i × ak → j (mod mod)

      那么用原根就可以将式子转化为 xp[i] × xp[ak] = xp[j] (mod mod)

      因为底数都是 x ,所以我们可以将其转化为指数间的运算

      即:p[ i ] + p[ ak ] = p[ j ] (mod φ(mod))

      

      哇,加法!

      好了,它循环了。

      为什么? 加法的转移相当与一种等距离的定向的转移,所以必循环

      时间复杂度变为 O ( mod2 log(m) ) ,同时还优化了空间的复杂度

      

      so,code

      

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<cstring>
     6 #include<vector>
     7 #include<queue>
     8 #define ll long long
     9 using namespace std;
    10 const int MAXMOD=1005,MAXN=100005,D=1e9+7;
    11 int n,m,mod;
    12 ll val[MAXN],ans,prt,pos[MAXMOD],cnt[MAXMOD],stk[MAXMOD],tp,invn;
    13 struct Matrix {
    14     ll s[MAXMOD];
    15     Matrix() {memset(s,0,sizeof(s));}
    16 }P,R;
    17 Matrix operator * (const Matrix &AA,const Matrix &BB) {
    18     Matrix CC;
    19     for(int i=0;i<mod;i++)
    20         for(int j=0;j<mod;j++)
    21             CC.s[(i+j)%(mod-1)]=(CC.s[(i+j)%(mod-1)]+AA.s[i]*BB.s[j]%D)%D;
    22     return CC;
    23 }    
    24 ll qpow(ll x,ll k,ll dd) {
    25     ll ret=1;
    26     while(k) {
    27         if(k&1) ret=(ret*x)%dd;
    28         x=(x*x)%dd,k>>=1;
    29     }
    30     return ret%dd;
    31 }
    32 void get_prt() {
    33     int tmp=mod-1;
    34     for(int i=2;i*i<mod;i++)
    35         if(tmp%i==0) {
    36             stk[++tp]=i;
    37             while(tmp%i==0) tmp/=i;
    38         }
    39     if(tmp>1) stk[++tp]=tmp;
    40     for(int i=2;i<=mod;i++) {
    41         bool flag=0;
    42         for(int j=1;j<=tp;j++)
    43             if(qpow(i,(mod-1)/stk[j],mod)==1) {flag=1;break;}
    44         if(!flag) {prt=i;break;}
    45     }
    46     for(int i=0;i<mod-1;i++) pos[qpow(prt,i,mod)]=i;
    47 }
    48 int main() {
    49     scanf("%d%d%d",&n,&m,&mod);
    50     for(int i=1;i<=n;i++) scanf("%lld",&val[i]),++cnt[val[i]];
    51     get_prt();
    52     invn=qpow(n,D-2,D);
    53     for(int i=1;i<mod;i++)
    54         P.s[pos[i]]=cnt[i]*invn%D;
    55     R.s[0]=1;
    56     while(m) {
    57         if(m&1) R=R*P;
    58         P=P*P,m>>=1;
    59     }
    60     for(int i=1;i<mod;i++) ans=(ans+R.s[pos[i]]*i)%D;
    61     printf("%lld
    ",ans);
    62     return 0;
    63 }
    t1 Code


    T2:给一颗树,每个节点都有两个权值a和b,a和b有如下关系:

      b[x]=a[1]dis(1,x)+a[2]dis(2,x)+....+a[n]*dis(n,x) (dis ( i , j ) 表示i 到j 的最短路径经过的边数)

      现在给你a与b数组中的一个,求另一个数组。

      ( 2<=n<=100000 )

      首先很容易就可以在 O ( n ) 的复杂度下利用换根dp用a求出b,dp方程显然,就不赘述了

      

      主要看如何用b来求a:

      不妨设dp的根为1

      我们看a求b时换根的方程 b[ v ] = b[ u ] + (siz[ 1 ] - siz [ v ])- siz [ v ]

      (siz [ i ] 表示以i为根的子树内a的和)(v是u的儿子)

      移项后得 b[ v ] - b[ u ] = siz[ 1 ] - 2×siz[ v ] 记作 g[ v ]

      于是我们可以对除1以外的所以点进行上述计算

      然后会发现其实并算不出什么东西???

      可是我们还没有用 b[ 1 ] 啊?

      于是将 b[ 1 ]的表达式写出,然后再合并

      发现,b[ 1 ] = ∑siz [ u ] (u!=1)

      那就简单了,用 ∑g[ v ] + 2 × b[ 1 ] 就可以计算出 siz[ 1 ] 的值

      然后再用 g 和 siz[ 1 ] 算出所有点的 siz

      最后dfs一遍求出a即可

      

      so,code

      

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<cstring>
     6 #include<vector>
     7 #include<queue>
     8 #define ll long long
     9 using namespace std;
    10 const int MAXN=200005;
    11 int T,n,o;
    12 ll val[MAXN],siz[MAXN],f[MAXN],g[MAXN],sum;
    13 struct node {
    14     int to,nxt;
    15 }mp[MAXN*2];
    16 int h[MAXN],tot;
    17 void add(int x,int y) {
    18     mp[++tot].nxt=h[x];
    19     mp[tot].to=y;
    20     h[x]=tot;
    21 }
    22 void dfs1(int u,int fa) {
    23     siz[u]=val[u];
    24     for(int i=h[u];i;i=mp[i].nxt) {
    25         int v=mp[i].to;
    26         if(v==fa) continue;
    27         dfs1(v,u);
    28         siz[u]+=siz[v];
    29         f[u]+=f[v]+siz[v];
    30     }
    31 }
    32 void dfs2(int u,int fa) {
    33     for(int i=h[u];i;i=mp[i].nxt) {
    34         int v=mp[i].to;
    35         if(v==fa) continue;
    36         g[v]=g[u]-siz[v]+(siz[1]-siz[v]);
    37         dfs2(v,u);
    38     }
    39 }
    40 void work0() {
    41     dfs1(1,0);
    42     g[1]=f[1];
    43     dfs2(1,0);
    44     for(int i=1;i<=n;i++) printf("%lld ",g[i]);
    45     printf("
    ");
    46 }
    47 void dfs3(int u,int fa) {
    48     for(int i=h[u];i;i=mp[i].nxt) {
    49         int v=mp[i].to;
    50         if(v==fa) continue;
    51         g[v]=val[v]-val[u];
    52         dfs3(v,u);
    53     }
    54 }
    55 void dfs4(int u,int fa) {
    56     f[u]=siz[u];
    57     for(int i=h[u];i;i=mp[i].nxt) {
    58         int v=mp[i].to;
    59         if(v==fa) continue;
    60         dfs4(v,u);
    61         f[u]-=siz[v];
    62     }
    63 }
    64 void work1() {
    65     dfs3(1,0);
    66     for(int i=2;i<=n;i++) sum+=g[i];
    67     siz[1]=(2*val[1]+sum)/(n-1);
    68     for(int i=2;i<=n;i++) siz[i]=(siz[1]-g[i])/2;
    69     dfs4(1,0);
    70     for(int i=1;i<=n;i++) printf("%lld ",f[i]);
    71     printf("
    ");
    72 }
    73 int main() {
    74     scanf("%d",&T);
    75     while(T--) {
    76         scanf("%d",&n);
    77         for(int i=1,aa,bb;i<n;i++) scanf("%d%d",&aa,&bb),add(aa,bb),add(bb,aa);
    78         scanf("%d",&o);
    79         for(int i=1;i<=n;i++) scanf("%lld",&val[i]);
    80         if(!o) work0();
    81         else work1();
    82         memset(h,0,sizeof(h));
    83         memset(f,0,sizeof(f));
    84         memset(g,0,sizeof(g));
    85         memset(siz,0,sizeof(siz));
    86         memset(val,0,sizeof(val));
    87         tot=0;sum=0;
    88     }
    89     return 0;
    90 }
    t2 Code


    T3:只能上下左右走,从(0,0)开始有限制的走n步之后回到(0,0)的方案数.

      一共有三种限制,加上没有限制的情况,一共有四种情况,用0,1,2,3标号:
      0.没有任何限制,可以到达坐标系上所有的点,即能到达的点集为{(x,y)|x,y为整数}
      1.只允许到达x轴非负半轴上的点.即能到达的点集为{(x,y)|x为非负数,y=0}
      2.只允许到达坐标轴上的点.即能到达的点集为{(x,y)|x=0或y=0}
      3.只允许到达x轴非负半轴上的点,y轴非负半轴上的点以及第1象限的点.即能到达的点集为{(x,y)|x>=0,y>=0}

      ( 对于 0,1,3 情况 n <= 100000 ,对于 2 情况 n <= 1000 )

      情况 0 :

        法一:可以很容易发现此题与noip模拟测试7中的T2就是一道题

           枚举一个方向的步数,计算出其他方向的步数,多重集排列一下就好了。

        法二:将坐标轴旋转45o (???)

           

           橙色线和蓝色线分别对应原坐标的坐标轴

           容易看出,在原坐标系中上下左右任意走一步可以对应在新坐标轴上连续走x,y方向各一步

           即:x,y两方向的行动顺序固定!

           那么两个行动方案不同当且仅当不同方向的步数不同!       

           那就简单了,总共走n步,所以转化过来就是x上走n步,y上走n步

           x上有n/2步是向上,n/2是向下,因为顺序一定所以方案数为C ( n , n/2 ),y 同理

           所以答案就是 [ C ( n , n/2 ) ]2 

      情况 1:显然,catalan数

      情况 2:加个dp,f [ i ] 表示走 2×i 步 的方案数

          注意到 n 在此情况情况较小,想 n2 dp

          首先可以看出行动的路径应该是从(0,0)走出,走回来,再转向;再走出……

          于是我们只需要枚举最后一次走回来的时刻就行了

          方程 :f [ i ] = ∑ f [ j ] × catlan [ i - j -1] × 4

                (…)   (出去再回来)(转向)

          为什么是catalan第 i - j - 1 项而不是 i - j 项?

          因为我们要使 j 是最后一个回到原点的时刻,所以我们不能再次走回原点

          而对于任意一种2×a步不回原点的方案,都唯一对应一种:

          先走出一步,再走 2×( a - 1 )步(可以回到出发点),最后走一步回来。

          所以方案数就为 catlan [ a - 1 ]

      情况 3 :这一看不就是两个catalan怎么计算一下就出来的吗?

          于是发现可以做个类似卷积的东西:

         ∑catalan [ i ] * catalan [ n/2 - i ] * ( 2 * n/2 ) ! / (2 * i) / ( 2 * ( n/2 - i ) )
         
         即:在 x 方向走 2×i 步,在 y 方向走 2×(n/2 - i) 步
         
         
         so,code
     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<cstring>
     6 #include<vector>
     7 #include<queue>
     8 #define ll long long
     9 using namespace std;
    10 const int MAXN=100105,D=1e9+7;
    11 ll n,op,ans,a,b,fac[MAXN],inv[MAXN],ct[MAXN],f[MAXN],g[MAXN];
    12 ll qpow(ll x,ll k) {
    13     ll ret=1;
    14     while(k) {
    15         if(k&1) ret=(ret*x)%D;
    16         x=(x*x)%D;
    17         k>>=1;
    18     }
    19     return ret%D;
    20 }
    21 void first(ll lim) {
    22     fac[0]=inv[0]=fac[1]=1;
    23     for(int i=2;i<=lim;i++) fac[i]=(fac[i-1]*i)%D;
    24     inv[lim]=qpow(fac[lim],D-2);
    25     for(int i=lim-1;i>=1;i--) inv[i]=(inv[i+1]*(i+1))%D;
    26 }
    27 void work0() {
    28     first(n);
    29     for(int i=0;i<=n;i+=2) {
    30         a=(ll)i/2;
    31         b=(ll)(n-i)/2;
    32         ans=(ans+fac[n]*inv[a]%D*inv[a]%D*inv[b]%D*inv[b])%D;
    33     }
    34     printf("%lld
    ",(ans%D+D)%D);
    35 }
    36 void work1() {
    37     first(n),n/=2;
    38     ans=fac[2*n]*inv[n]%D*inv[n+1]%D;
    39     printf("%lld
    ",ans);
    40 }
    41 void work2() {
    42     first(n),n/=2;
    43     ct[0]=1;
    44     for(int i=1;i<=n;i++) ct[i]=fac[2*i]*inv[i+1]%D*inv[i]%D;
    45     f[0]=1;
    46     for(int i=1;i<=n;i++)
    47         for(int j=0;j<=i;j++) f[i]=(f[i]+f[j]*ct[i-j-1]%D*4)%D;
    48     printf("%lld
    ",f[n]);
    49 }
    50 void work3() {
    51     first(n),n/=2;
    52     ct[0]=1;
    53     for(int i=1;i<=n;i++) ct[i]=fac[2*i]*inv[i+1]%D*inv[i]%D;
    54     for(int i=0;i<=n;i++)
    55         ans=(ans+ct[i]*ct[n-i]%D*fac[2*n]%D*inv[2*i]%D*inv[2*(n-i)]%D)%D;
    56     printf("%lld
    ",ans);
    57 }
    58 int main() {
    59     scanf("%lld%lld",&n,&op);
    60     if(op==0) work0();
    61     if(op==1) work1();
    62     if(op==2) work2();
    63     if(op==3) work3();
    64     return 0;
    65 }
    t3 Code

  • 相关阅读:
    Java安全之JNDI注入
    Visual Studio 2019 升级16.8之后(升级.Net 5),RazorTagHelper任务意外失败
    .Net Core 3.1升级 .Net 5后出现代码错误 rzc generate exited with code 1.
    重走py 之路 ——普通操作与函数(三)
    重走py 之路 ——字典和集合(二)
    设计模式结(完结篇)
    重走py 之路 ——列表(一)
    RestfulApi 学习笔记——分页和排序(五)
    RestfulApi 学习笔记——查询与过滤还有搜索(五)
    Android开发 Error:The number of method references in a .dex file cannot exceed 64K.Android开发 Error:The number of method references in a .dex file cannot exceed 64K
  • 原文地址:https://www.cnblogs.com/Gkeng/p/11259008.html
Copyright © 2011-2022 走看看