[UOJ607] 跳蚤电话 题解

很神秘很神秘qwq。


考虑正难则反:既然我们难以正向推导,何不转化为删边?既然我们难以直接算出答案,何不算出期望?于是我们便有了基础思路:设 \(f_i\) 表示假如子树 \(i\) 的父亲不得删去,那么随机排列能够删空子树 \(i\) 的概率是多少。那么答案显然是 \((n-1)!\prod\limits_{i\in sn_1}f_i\)

考虑对于最后一个删去的节点进行分类讨论:

  1. 最后一个删去的节点为 \(i\)。显然答案为 \(\prod\limits_{j\in sn_i}f_j\)
  2. 最后一个删去的点为 \(j\ne i\)。考虑分成两个部分:
    1. \(j\) 的子树内。显然是 \(\prod\limits_{k\in sn_j}f_k\)
    2. 其他部分的话,考虑枚举祖先,设 \(a_1=i,\dots,a_{c-1}=fa_j,a_c=j\)。既然节点 \(i\) 的父亲不可删去,并且 \(j\) 最后删去,那么删去一个 \(a_x\) 的时候,这个点的度数一定是 \(2\),而且 \(a_x\) 的子树内,除了 \(a_{x+1}\) 子树内的点一定都被删了个精光。所以这部分概率是:

      \[\prod_{u=1}^{c-1}(\prod_{v\in sn_{a_u},v\ne a_{u+1}}\frac{f_v}{sz_{a_u}-sz_{a_{u+1}}}) \]

所以转移方程即为:

\[\begin{aligned} f_i&=\frac{1}{sz_i}(\prod\limits_{j\in sn_i}f_j+\sum_{j\in subtree_i}\prod_{u=1}^{c_j-1}(\prod_{v\in sn_{a_{j,u}},v\ne a_{j,u+1}}\frac{f_v}{sz_{a_{j,u}}-sz_{a_{j,u+1}}})\\ &=\frac{1}{sz_i}(\prod_{j\in sn_i}f_j+\sum_{j\in sn_i}f_jsz_j\prod_{k\in sn_i,k\ne j}\frac{f_k}{sz_i-sz_j})\\ &=\prod_{j\in subtree_i}\frac{1+\sum\limits_{k\in sn_j}\dfrac{sz_j}{sz_j-sz_k}}{sz_j} \end{aligned}\]

其实倒数第二步的式子已经够用了。最后两个式子的时间复杂度都是 \(O(n)\)

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,p=998244353;
int n,inv[N],sz[N],ans;vector<int>g[N];
void dfs(int x,int fa){
	int m=g[x].size();
	for(int i=0;i<m;i++) if(g[x][i]!=fa)
		dfs(g[x][i],x),sz[x]+=sz[g[x][i]];
	long long now=1;sz[x]++;
	for(int i=0;i<m;i++) if(g[x][i]!=fa)
		now+=1ll*sz[g[x][i]]*inv[sz[x]-sz[g[x][i]]];
	ans=1ll*now%p*ans%p*inv[sz[x]]%p;
}int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n,inv[1]=ans=1;
	for(int i=2;i<=n;i++)
		inv[i]=1ll*(p-p/i)*inv[p%i]%p;
	for(int i=2;i<n;i++) ans=1ll*ans*i%p;
	for(int i=1,x,y;i<n;i++)
		cin>>x>>y,g[x].push_back(y),g[y].push_back(x);
	for(auto x:g[1]) dfs(x,1);
	return cout<<ans,0;
}
posted @ 2025-03-25 17:45  长安一片月_22  阅读(15)  评论(0)    收藏  举报