[cf674E]Bear and Destroying Subtrees

令$f_{i,j}$表示以$i$为根的子树中,深度小于等于$j$的概率,那么$ans_{i}=\sum_{j=1}^{dep}(f_{i,j}-f_{i,j-1})j$

大约来估计一下$f_{i,j}$的大小,较坏情况下是$\lfloor\frac{n-1}{j}\rfloor$个深度为$j$的节点(若选择有公共部分,虽然会增加节点数但并实际边的数量减少),即可以认为$f_{i,j}\ge (1-\frac{1}{2^{j}})^{\lfloor\frac{n-1}{j}\rfloor}$

其在$j\ge 60$时,可以认为$f_{i,j}=1$,代入$ans_{i}$的式子即$j>60$的部分不需要计算

因此此时状态数为$o(Dn)$(其中$D=60$),接下来考虑如何维护

如果对其暴力dp,转移为$f_{i,j}=\frac{1}{2}f_{i,j}(1+f_{son,j-1})$,那么当插入节点的$k$,显然会修改$f_{fa_{k},0}$、$f_{fa_{fa_{k}},1}$等位置,因此插入复杂度为$o(D)$,查询时对该位置$f$求和同样为$o(D)$

总复杂度为$o(Dn)$,可以通过

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 500005
 4 #define D 50
 5 vector<int>v;
 6 int V,n,p,x,fa[N];
 7 double ans,f[N][D+5];
 8 void New(int k){
 9     fa[++V]=k;
10     for(int i=0;i<=D;i++)f[V][i]=1;
11 }
12 int main(){
13     scanf("%d",&n);
14     New(0);
15     for(int i=1;i<=n;i++){
16         scanf("%d%d",&p,&x);
17         if (p==1){
18             New(x);
19             for(int j=1,k=x;(j<=D)&&(k);j++,k=fa[k])v.push_back(k);
20             while (v.size()){
21                 f[fa[v.back()]][v.size()]/=1+f[v.back()][(int)v.size()-1];
22                 v.pop_back();
23             }
24             f[x][0]/=2;
25             for(int j=1,k=x;(j<=D)&&(k);j++,k=fa[k])f[fa[k]][j]*=1+f[k][j-1];
26         }
27         else{
28             ans=0;
29             for(int j=1;j<=D;j++)ans+=(f[x][j]-f[x][j-1])*j;
30             printf("%.9f\n",ans);
31         }
32     }
33 }
View Code

 

posted @ 2021-04-08 15:28  PYWBKTDA  阅读(63)  评论(0编辑  收藏  举报