1. 程式人生 > 其它 >2022牛客多校3 D 期望概率dp

2022牛客多校3 D 期望概率dp

D

簡述一下題意:

給定一顆以1為根的樹 一個起點x 樹上有k隨機條邊定向變為兒子到父親。

求從x出發到達1號節點的期望步數。

這個期望題很好。

先考慮0條邊定向x到1的期望步數 顯然設f[x]表示x到達父親的期望步數 然後一路加上去即可。

這個f[x]很好求 同時也有一個比較好的式子\(f[x]=2*sz[x]-1\)

給出f[x]的計算式:設d[x]為x這個點的度數則\(f[x]=d[x]+\sum_{tn\in son_x} f[tn]\)

在考慮一條邊定向的影響 設u,v邊定向且u是v的父親 那麼相當於f[u]少了v這個兒子帶來的影響。

進一步的可以發現影響為\(f[u]-=2*sz[v]\)

這樣我們正著思考 暴力列舉k條邊 就可以把現在樹的情況的答案給算出來。

再由和的期望=期望的和 考慮每一條邊的貢獻

可以想到這還不夠 單獨考慮一條邊被定向還需要知道其他邊的資訊才能得到貢獻。

同時一條邊的定向帶來的是整體代價-損失。

所以直接考慮總答案-k條邊被定向的期望損失 這會很自然。

接下來考慮一條邊被定向的損失 此時要算這個東西還要知道它的祖先是否被定向才能計算成功。

兩種考慮方式列舉祖先被定向的那條邊 計算概率 同時貢獻為它到祖先這一段距離。

由於存在兩種邊一種在初始鏈上一種不在初始鏈上 這種計算方法為\(n^2\)

另外一種方法:列舉當前被定向的邊對向上第j條邊產生損失貢獻的概率

可以發現兩種計算出來的東西相同前者是整體計算 後者是單點計算。

但是後者的可以進行字首和優化 複雜度O(n)

code
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cctype>
#include<queue>
#include<deque>
#include<stack>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 2000000000
#define inf 100000000000000000ll
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define get(x) x=read()
#define putl(x) printf("%lld\n",x)
#define rep(p,n,i) for(int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define pii pair<int,int>
#define mk make_pair
#define P 1000000007ll
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define sq sqrt
#define y(w) t[w].y
#define x(w) t[w].x
#define z(w) t[w].z
#define id(cc) s[cc].id
#define S second
#define mod 998244353
#define sc(A) scanf("%d",&A)
#define scs(A) scanf("%s",A);
#define put(A) printf("%d\n",A)
#define min(x,y) (x>=y?y:x)
#define max(x,y) (x>=y?x:y)
using namespace std;
const int MAXN=1000010,maxn=1000010;
int n,k,s,kk,len;int ans;
int f[MAXN],sz[MAXN],d[MAXN],fa[MAXN],c[MAXN];
int lin[MAXN],ver[MAXN<<1],nex[MAXN<<1];
int fac[MAXN],inv[MAXN],p[MAXN];
inline void add(int x,int y)
{
	ver[++len]=y;
	nex[len]=lin[x];
	lin[x]=len;
}
inline int C(int n,int m)
{
	if(m>n)return 0;
	if(m<0)return 0;
	return (ll)fac[n]*inv[m]%mod*inv[n-m]%mod;
}
//f[i]表示從i到父親的期望步數.
inline void dfs(int x,int fa1)
{
	sz[x]=1;
	go(x)
	{
		if(tn==fa1)continue;
		d[tn]=d[x]+1;
		fa[tn]=x;
		dfs(tn,x);
		sz[x]+=sz[tn];
	}
	f[x]=2*sz[x]-1;
}
inline void dp(int x,int lca)
{
	if(lca)
	{
		if(c[x])
		{
			ans=(ans-(ll)(f[x]+1)*p[d[x]-1]%mod+mod)%mod;
		}
		else
		{
			ans=(ans-(ll)(f[x]+1)*(p[d[x]-1]-p[d[x]-d[lca]-1]+mod)%mod+mod)%mod;
		}
	}
	if(lca==0&&x!=1)return;
	go(x)
	{
		if(tn==fa[x])continue;
		if(c[tn])dp(tn,tn);
		else dp(tn,lca);
	}
}
inline int ksm(int b,int p)
{
	int cnt=1;
	while(p)
	{
		if(p&1)cnt=(ll)cnt*b%mod;
		b=(ll)b*b%mod;p=p>>1;
	}
	return cnt;
}
int main()
{
	//freopen("1.in","r",stdin);
	sc(n);sc(k);sc(s);
	fac[0]=1;
	rep(1,n,i)fac[i]=(ll)fac[i-1]*i%mod;
	inv[n]=ksm(fac[n],mod-2);
	for(int i=n-1;i>=0;--i)inv[i]=(ll)inv[i+1]*(i+1)%mod;
	kk=C(n-1,k);kk=ksm(kk,mod-2);
	rep(2,n,i)
	{
		int x,y;
		sc(x);sc(y);
		add(x,y);
		add(y,x);
	}
	dfs(1,0);
	int w=s;
	while(w!=1)
	{
		ans=(ans+f[w])%mod;
		c[w]=1;
		w=fa[w];
	}
	rep(1,n,i)p[i]=((ll)C(n-1-i,k-1)*kk+p[i-1])%mod;
	dp(1,0);
	put(ans);
	return 0;
}