牛客周賽 Round50 E-小紅的樹上移動 (期望dp+逆元)

栗悟饭と龟波功發表於2024-07-09

image

E-小紅的樹上移動

題目:image

題意:在一個樹上從根節點移動,每次都會向更深的下一層走,如果此時已經是葉子節點沒有下一層就會停留在這裡。求出移動次數的期望,移動次數就是從根節點1開始到此節點的深度。

思路:image

畫一個草圖不難看出其實在同一層中,到達每個點的機率是一樣的。並且,對於每一層的節點機率就是這一層節點個數乘能到達這一層的機率。其中能到達這一層的機率就是到達上一層的機率乘上一層的(非葉子點數/總點數)。

那麼,總結一下我們能得到如此的遞推關係dp[i]=dp[i-1]*(a-b)/a

dp[i]為到達i+1層的機率,a為總點數,b為葉子點數

那麼我們要計算的答案期望就是dp[i-1]乘(b/a)乘d[i],d[i]就是這一層的深度

以下是程式碼,!注意求逆元時的取模問題

點選檢視程式碼
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl "\n"
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const int N = 2e6+10;
const int p = 998244353;
int f[N],inv[N],yz[N],dep[N];
int km(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1) res=res*a%p;
		a=a*a%p;
		b>>=1;
	}
	return res;
}
int n,d[N],dp[N],mx=0;
vector<int> h[N];
bool vis[N];
void add(int a,int b)
{
	h[a].push_back(b);
	h[b].push_back(a);
} 
void bfs()
{
	queue<int> q;
	q.push(1);
	vis[1]=1,d[1]=0;
	while(q.size())
	{
		int u=q.front();
		q.pop();
		for(auto v:h[u])
		{
			if(vis[v]) continue;
			vis[v]=1;
			d[v]=d[u]+1;//算出深度 
			dep[d[v]]++;//紀錄每個深度點的個數 
            if(h[v].size()==1&&v!=1) yz[d[v]]++;//紀錄葉子節點個數 
			mx=max(mx,d[v]);//紀錄下最大深度 
			q.push(v);
		}
	}
}
void solve()
{
	cin>>n;
	for(int i=1;i<n;i++) {
		int u,v;
		cin>>u>>v;
		add(u,v);
	}
	bfs();
	dp[0]=1;
	int ans=0;
    for(int i=1;i<=mx;i++)//遞推求答案過程,注意求逆元時取模 
    {
        int a=dep[i],b=yz[i];
        ans+=i%p*dp[i-1]%p*b%p*km(a,p-2)%p;
        ans%=p;
        dp[i]=dp[i-1]%p*(a-b)%p*km(a,p-2)%p;
        dp[i]%=p;
    }
	cout<<ans<<endl;
}
signed main()
{
	ios;
	int t=1;
	//cin>>t;
	while(t--)
	{
		solve() ;
	}
	return 0;
}

El Psy Congroo" - Okabe Rintaro!

相關文章