2022牛客多校3 D 期望概率dp
简述一下题意:
给定一颗以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;
}