2022牛客多校3 D 期望概率dp
阿新 • • 發佈:2022-12-08
簡述一下題意:
給定一顆以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; }