Tyvj1953 Normal 题解

_ANIG_

2023-09-15 22:02:49

Personal

神仙题。

题目

题意

求一个错误淀粉质的复杂度。

题解

对于每个点分开考虑。这个点对答案的贡献是期望在多少次递归后被删除。

假设当前考虑的节点是根节点。

如果删除树上一个节点,树会分成若干个连通块。我们要考虑根节点所在的连通块。显然,这个连通块是被删除节点的父节点所处的连通块。也就是说删一个点等价于删除这个点的子树。

这样,题目就转化为:每次删除一个未被删除的节点的子树,求删除根节点的期望次数。

此时,题目就和 Game on tree 一模一样。

考虑每个点的贡献。如果这个点在操作序列中在所有祖先的前面,就对答案有贡献。概率就是祖先的个数的倒数,也就是深度的倒数。

答案就是所有节点的深度的倒数和,也就是所有点到根结点的距离的倒数和。

把每个点作为根节点考虑,把答案加起来,可以发现答案就是任意两个点的距离加 1 的倒数和。

考虑点分治。

求穿过根节点的路径长度加 1 的倒数和。

预处理子树中每个节点的深度,答案就是任意两个不在同一个子树内的点的深度和加 1 的倒数和。可以容斥。答案就是任意两个点的深度和加 1 的倒数和减去每个子树内深度和加 1 的倒数和。

问题转化为:\sum\limits_{i=1}^n\sum\limits_{j=1}^n\frac1{dep_i+dep_j+1}

统计深度为 i 的点的数量 c_i,答案为:

\sum\limits_{i=1}^m\sum\limits_{j=1}^m\frac1{i+j+1}c_ic_j 枚举 $i+j$,答案为 $\sum\limits_{k=1}^{2m}\frac1{k+1}\sum\limits_{i=1}^kc_ic_{k-i}$。 右边的东西可以直接 FFT。 时间复杂度 $O(n\log^2n)$。 ```cpp #include <bits/stdc++.h> using namespace std; #define int long long const int mods=998244353; int pows(int a,int b){ if(b==0)return 1; int res=pows(a,b>>1); res=res*res%mods; if(b&1)res=res*a%mods; return res; } const int n1=3,n2=pows(3,mods-2); int r[1000005]; void fft(int *p,int n,int op){ int bt=1; while((1<<bt)<n)bt++; for(int i=0;i<n;i++){ r[i]=(r[i>>1]>>1)|((i&1)<<bt-1); if(i<r[i])swap(p[i],p[r[i]]); } for(int mid=1;mid<n;mid<<=1){ int tmp=pows(op==1?n1:n2,(mods-1)/(mid<<1)); for(int i=0;i<n;i+=mid<<1){ int om=1; for(int j=0;j<mid;j++,om=om*tmp%mods){ int x=p[i+j],y=p[i+j+mid]*om%mods; p[i+j]=(x+y)%mods,p[i+j+mid]=(x-y)%mods; } } } } int gets(int x){ int res=1; while((1<<res)<=x)res++; return 1<<res; } double res; int n,siz[100005],zd[100005],zx,mk[100005],bk[100005],dep[100005],c[100005],cr[100005],md,mdx; vector<int>p[100005],jl; void dfs1(int x,int y){ zd[x]=0; mk[x]=1;siz[x]=1; for(int i=0;i<p[x].size();i++){ int c=p[x][i]; if(mk[c]||bk[c])continue; dfs1(c,y); siz[x]+=siz[c]; zd[x]=max(zd[x],siz[c]); } zd[x]=max(zd[x],y-siz[x]); if(zx==0||zd[zx]>zd[x])zx=x; mk[x]=0; } void dfs2(int x){ mk[x]=1;siz[x]=1; c[dep[x]]++;cr[dep[x]]++; md=max(md,dep[x]); mdx=max(mdx,dep[x]); jl.push_back(x); for(int i=0;i<p[x].size();i++){ int c=p[x][i]; if(mk[c]||bk[c])continue; dep[c]=dep[x]+1; dfs2(c); siz[x]+=siz[c]; } mk[x]=0; } void solve(int x){ if(siz[x]==1)return; bk[x]=1;md=0;siz[x]=1; for(int i=0;i<p[x].size();i++){ int c=p[x][i]; if(bk[c])continue; dep[c]=1;mdx=0; dfs2(c); int m=gets(2*mdx); fft(cr,m,1); for(int i=0;i<=m;i++)cr[i]=cr[i]*cr[i]%mods; fft(cr,m,-1); int ny=pows(m,mods-2); for(int i=0;i<=m;i++)cr[i]=cr[i]*ny%mods,cr[i]=(cr[i]+mods)%mods; for(int i=1;i<=m;i++)res-=1.0/(i+1)*cr[i]; for(int i=0;i<=m;i++)cr[i]=0; siz[x]+=siz[c]; } int m=gets(2*md); c[0]=1; fft(c,m,1); for(int i=0;i<=m;i++)c[i]=c[i]*c[i]%mods; fft(c,m,-1); int ny=pows(m,mods-2); for(int i=0;i<=m;i++)c[i]=c[i]*ny%mods,c[i]=(c[i]+mods)%mods; for(int i=1;i<=m;i++)res+=1.0/(i+1)*c[i]; for(int i=0;i<=m;i++)c[i]=0; for(int i=0;i<p[x].size();i++){ int c=p[x][i]; if(bk[c])continue; zx=0; dfs1(c,siz[x]); solve(zx); } } signed main(){ cin>>n; for(int i=1;i<n;i++){ int x,y; scanf("%lld%lld",&x,&y); x++,y++; p[x].push_back(y); p[y].push_back(x); } solve(1); printf("%.4lf",res+n); } ```