《死亡迴圈》工作室領導離開 曾任職將近17年
阿新 • • 發佈:2021-10-07
永遠學不會數學.jpg
,初值 \(f_{u,j} = p_{u,j}\cdot j(j \le 3)\)。
沒想到最後一步。還是太菜了。
簡要題意
-
\(n\) 種卡牌,每一輪抽到第 \(i\) 種卡牌的概率為 \(\dfrac{w_i}{\sum w_j}\),其中 \(\forall j\in\{1,2,3\}\),\(w_i\) 有 \(p_{i,j}\) 的概率為 \(j\)。
-
設第一次抽到 \(i\) 的時間為 \(T_i\),有 \(n-1\) 對限制構成一棵有向樹,滿足對於所有樹邊 \(u_i\to v_i\),\(T_{u_i} \lt T_{v_i}\)。求滿足所有限制的概率。
-
\(n \le 1000\)
題解
若欽定樹根,這棵樹有著紛繁錯雜的連邊關係:根向、葉向可以交替出現,這就為解題帶來很大不便。為此,先考慮所有邊方向一致(不妨假定為根向,葉向同理)。
需要注意的是,每種卡牌出現的概率並不獨立,這就意味著不能用簡單的期望代替 \(w_i\)。由於邊是根向的,題目中的限制等價於:任意結點先於子樹中的結點出現(記為條件 A)。而當子樹中的 \(w_i\) 確定時,條件 A 成立的概率是確定的:\(P(A) = \dfrac{w_u}{\sum\limits_{v\in \text{subtree}\ u}w_v}\)。因此只需記錄子樹的 \(w_v\) 之和即可 dp:\(f_{u,i}\) 表示以 \(u\) 為根的子樹中,\(w_v\) 之和為 \(i\) 且在該子樹中滿足所有限制的概率。則: \(f^{'}_{u,i+j} = \dfrac{1}{i+j}\sum_{i,j}f_{u,i}\cdot f_{v,j}\)
當加入反向邊之後,情況就不再這麼簡單——直接做會導致係數亂套。考慮一個容斥:\(P(反向) = P(不存在)-P(正向)\) 。於是在樹邊上記一個容斥係數即可 dp。
Code
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAXN = 1010; const int MOD = 998244353; inline int add(int a, int b) {return (a+=b)>=MOD?a-MOD:a;} inline void inc(int& a, int b) {a = add(a, b);} inline int sub(int a, int b) {return (a-=b)<0?a+MOD:a;} inline void dec(int& a, int b) {a = sub(a, b);} inline int mul(int a, int b) {return 1ll*a*b%MOD;} inline void mlt(int& a, int b) {a = mul(a, b);} inline int pw(int x, int p = MOD-2) { int res = 1; for(;p;p>>=1,mlt(x,x)) if(p&1) mlt(res, x); return res; } int inv[MAXN*3]; inline void initInv(int n) { inv[1] = 1; for(int i=2;i<=n;i++) inv[i] = mul(MOD-MOD/i, inv[MOD%i]); } int n, p[MAXN][4]; struct edge{ int ne, to, w; edge(int N=0,int T=0,int W=0):ne(N),to(T),w(W){} }e[MAXN<<1]; int fir[MAXN], num = 0; inline void join(int a, int b, int c) { e[++num] = edge(fir[a], b, c); fir[a] = num; } int f[MAXN][MAXN*3], siz[MAXN], tmp[MAXN*3]; void dfs(int u, int fa) { for(int i=1;i<=3;i++) f[u][i] = mul(p[u][i], i); siz[u] = 3; for(int i=fir[u];i;i=e[i].ne) { int v = e[i].to; if(v == fa) continue; dfs(v, u); memset(tmp, 0, (siz[u]+siz[v]+2)<<2); for(int j=1;j<=siz[u];j++) for(int k=1;k<=siz[v];k++) { inc(tmp[j+k], mul(mul(f[u][j], f[v][k]), e[i].w)); if(e[i].w != 1) inc(tmp[j], mul(f[u][j], f[v][k])); } siz[u] += siz[v]; for(int j=1;j<=siz[u];j++) f[u][j] = tmp[j]; } for(int i=1;i<=siz[u];i++) mlt(f[u][i], inv[i]); } inline void work() { scanf("%d",&n); initInv(n*3); for(int i=1;i<=n;i++) { int s = 0; for(int j=1;j<=3;j++) scanf("%d",p[i]+j), inc(s, p[i][j]); s = pw(s); for(int j=1;j<=3;j++) mlt(p[i][j], s); } for(int i=1,u,v;i<n;i++) { scanf("%d%d",&u,&v); join(u, v, 1); join(v, u, MOD-1); } dfs(1, 0); int ans = 0; for(int i=1;i<=n*3;i++) inc(ans, f[1][i]); printf("%d\n",ans); } int main() { int T = 1; // scanf("%d%d",&T,&MOD); // prework(N); while(T--) work(); return 0; }