• 7.14T2


    熟练剖分?什么鬼,我没学过啊,然后在经历了一番深入读题之后,发现这就是道概率期望题,果断丢掉,最后骗了15分

    回到正题,首先理解一下题意,它要给每个父节点选一个重儿子,然后算整个树中最多能经过多少轻边,最后要求算的是这个树中最多经过多少轻边的期望值,这个好说,就是每种不同的分配重儿子的概率,乘以这种情况下经过最多轻边的边数,那这个概率怎么求呢?显然树上DP

    由于他最后是分子乘上分母的逆元,那我们就不设概率,改设方案数,这样的话用方案数处以整棵树的方案数就是我们要的概率,设g[i][0/1][j]表示某个父节点的第i个及其之前的儿子们,最长轻链长度为j的方案数,这个0/1是0代表在这个儿子及其之前还没有选过重儿子,1就代表已经选过重儿子,用f[x][j]表示以x为根的子树中最长轻链长度为j的方案数,我们来考虑一下怎么转移

    转移肯定是以DFS为载体,我们假设现在搜到了x的第i个儿子那么,前i-1个儿子中最长轻链长度为j,第i个儿子的最长轻链长度为k,则有(注意区分0和1)

    g[i][0][max(j,k+1)]+=g[i-1][0][j]*f[i][k]  ————(1)

    g[i][1][max(j,k+1)]+=g[i-1][1][j]*f[i][k]  ————(2)

    g[i][1][max(j,k)]+=g[i-1][0][j]*f[i][k]      ————(3)

    观察一下这三个转移方程,其实很好理解,我们先看第一个式子,当目前到这个儿子都没有出现重儿子时,证明这个儿子是个轻儿子,那么他对他父亲的贡献应该是他自己的轻链长度+1,因为他到他父亲也是一个轻链,关于这个乘,其实很好理解,但是我卡了很久,感谢secret给我举的例子以及itawbm同学的友情出演解决了我的问题,其实不管是j大还是k+1大,因为你算的是方案数,所以不管你较小的的那个数比max小多少,他都构成了我的这个max值,就好比在2,5,8,9中选两个数保证这两个数的最大值为9,那只要你选了9,剩下那个数选谁都无所谓,但他们是不同的方案,所以这个地方是乘

    第二,三个式子都是对于当到i时有重儿子说的,因为你不知道是在第i个儿子之前就已经出现了重儿子还是这第i个儿子就是重儿子,所以就要分出来这两种情况,而这两种情况的差别在于第i个儿子对于x的贡献,如果是之前就已经出现了重儿子,那第i个儿子和x之间就是一条轻边,那贡献k就需要+1,如果就是第i个儿子做重儿子,那么他和x之间的连边就不构成贡献

    这样的话转移方程就结束了,这道题还可以用滚动数组优化g的空间,然后记录一下每个点的子树深度,来减少k和j的循环

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<vector>
     4 #include<cstring>
     5 #define maxn 3010
     6 #define ll long long
     7 using namespace std;
     8 const long long mod=1e9+7;
     9 int n,root;
    10 ll ny,ans,tot=1;
    11 int fa[maxn],deep[maxn];
    12 ll zz[2][2][maxn],fen[maxn][maxn];
    13 vector <int> son[maxn];
    14 ll ksm(ll a,ll b)
    15 {
    16     ll fan=1;  a=a%mod;
    17     while(b>0)
    18     {
    19         if(b&1)  fan=(fan*a)%mod;
    20         b=b>>1;  a=(a*a)%mod;
    21     }
    22     return fan%mod;
    23 }
    24 void dfs(int x)
    25 {
    26     if(son[x].size()==0)  {fen[x][0]=1;  return ;}
    27     for(int i=0;i<son[x].size();++i)  {int ls=son[x][i];  dfs(ls);  deep[x]=max(deep[x],deep[ls]+1);}
    28     int cur=0;
    29     memset(zz,0,sizeof(zz));
    30     for(int j=1;j<=deep[x];++j)  {zz[cur][0][j]=fen[son[x][0]][j-1];  zz[cur][1][j]=fen[son[x][0]][j];}
    31     zz[cur][1][0]=fen[son[x][0]][0];
    32     int maxx=deep[son[x][0]]+1;
    33     for(int i=1;i<son[x].size();++i)
    34     {
    35         cur=cur^1;  memset(zz[cur],0,sizeof(zz[cur]));
    36         for(int j=0;j<=maxx;++j)
    37             for(int k=0;k<=deep[son[x][i]];++k)
    38             {
    39                 zz[cur][0][max(j,k+1)]=(zz[cur][0][max(j,k+1)]+(zz[cur^1][0][j]*fen[son[x][i]][k])%mod)%mod;
    40                 zz[cur][1][max(j,k+1)]=(zz[cur][1][max(j,k+1)]+(zz[cur^1][1][j]*fen[son[x][i]][k])%mod)%mod;
    41                 zz[cur][1][max(j,k)]=(zz[cur][1][max(j,k)]+(zz[cur^1][0][j]*fen[son[x][i]][k])%mod)%mod;
    42             }
    43         maxx=max(maxx,(deep[son[x][i]]+1));
    44     }
    45     memcpy(fen[x],zz[cur][1],sizeof(zz[cur][1]));
    46 }
    47 int main()
    48 {
    49     scanf("%d",&n);
    50     for(int i=1;i<=n;++i)
    51     {
    52         int s;  scanf("%d",&s);
    53         if(s!=0)  tot=(tot*(ll)s)%mod;
    54         for(int j=1;j<=s;++j)  {int a;  scanf("%d",&a);  fa[a]=i;  son[i].push_back(a);}
    55     }
    56     for(int i=1;i<=n;++i)
    57         if(fa[i]==0)  root=i;
    58     dfs(root);
    59     ny=ksm(tot,mod-2);
    60     for(int j=0;j<=deep[root];++j)  ans=(ans+(fen[root][j]*j)%mod)%mod;
    61     ans=(ans*ny)%mod;
    62     printf("%lld
    ",ans);
    63     return 0;
    64 }
    View Code
  • 相关阅读:
    java基础学习总结——哈希编码
    java基础学习总结——static关键字
    java基础学习总结——方法的重载(overload)
    java基础学习总结——流
    java基础学习总结——线程(二)
    java基础学习总结——线程(一)
    阻塞队列---ArrayBlockingQueue,LinkedBlockingQueue,DelayQueue源码分析
    hashCode 一致性hash 算法
    byte以及UTF-8的转码规则
    vue cli 解决跨域 线上 nginx 反向代理配置
  • 原文地址:https://www.cnblogs.com/hzjuruo/p/11201314.html
Copyright © 2020-2023  润新知