[CTS2019]氪金手遊
阿新 • • 發佈:2021-06-23
Link
Solution
先考慮外向樹的情況,這樣根一定是最小的,所有子節點都必須比它後訪問,而所有非子節點多久抽到無所謂。假定每個點的 \(w_i\) 是定值,\(s_i\) 是 \(i\) 的子樹和,\(S\) 是總和,那麼概率就是
\[\prod_{i=1}^n \frac{w_i}{S} \sum_{j=0}^{\infty} (\frac{S-s_i}{S})^{j}=\prod_{i=1}^n \frac{w_i}{s_i} \]發先最後答案只和每個點的 \(w\) 和 \(s\) 有關。考慮把 \(w\) 和 \(s\) 壓入狀態進行 dp,然後發現 \(w\) 可以去掉。記 \(dp[u][x]\)
再考慮加入反向邊,一個容斥小技巧,將反向邊拆成不考慮這條邊(分成兩個連通塊),減去這條邊是正向邊的情況。後者就是外向樹的情況,前者將其分成兩個連通塊,容易發現這兩個連通塊互不干擾,所以概率就是兩個分別 dp 後的乘積,也即一個外向樹森林。那麼最後的概率就可以通過列舉每條反向邊的方向,然後 dp,再乘上一個容斥係數,求和。複雜度 \(O(2^n n^2)\)
又發現我們並不在意每條邊具體是什麼方向,容斥係數只和反向邊沒有反向的數量有關,而 dp 轉移只和 \(w\)
#include<stdio.h> const int N=1e3+7; const int M=3e6+7; const int Mod=998244353; struct E{ int next,to; bool tag; }e[N<<1]; int head[N],cnt=0,n; int inv[M],sz[N],dp[N][N*3],tmp[N*3]; inline void add(int id,int to){ e[++cnt]=(E){head[id],to,0}; head[id]=cnt; e[++cnt]=(E){head[to],id,1}; head[to]=cnt; } void dfs(int u,int fa){ sz[u]=1; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(fa==v) continue; dfs(v,u); for(int x=1;x<=sz[u]*3;x++) for(int y=1;y<=sz[v]*3;y++){ int t=1ll*dp[u][x]*dp[v][y]%Mod; if(e[i].tag) tmp[x+y]=(tmp[x+y]-t+Mod)%Mod,tmp[x]=(tmp[x]+t)%Mod; else tmp[x+y]=(tmp[x+y]+t)%Mod; } sz[u]+=sz[v]; for(int x=1;x<=sz[u]*3;x++) dp[u][x]=tmp[x],tmp[x]=0; } for(int i=1;i<=sz[u]*3;i++) dp[u][i]=1ll*dp[u][i]*inv[i]%Mod; } int main(){ scanf("%d",&n); inv[1]=1; for(int i=2;i<M;i++) inv[i]=1ll*(Mod-Mod/i)*inv[Mod%i]%Mod; for(int i=1,a,b,c;i<=n;i++){ scanf("%d%d%d",&a,&b,&c); int w=inv[a+b+c]; dp[i][1]=1ll*a*w%Mod; dp[i][2]=1ll*b*w%Mod*2ll%Mod; dp[i][3]=1ll*c*w%Mod*3ll%Mod; } for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),add(u,v); dfs(1,0); int ans=0; for(int i=1;i<=3*n;i++) ans=(ans+dp[1][i])%Mod; printf("%d",ans); }