1. 程式人生 > 遊戲 >《死亡迴圈》工作室領導離開 曾任職將近17年

《死亡迴圈》工作室領導離開 曾任職將近17年

永遠學不會數學.jpg

沒想到最後一步。還是太菜了。

簡要題意

  • \(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}\)

,初值 \(f_{u,j} = p_{u,j}\cdot j(j \le 3)\)

當加入反向邊之後,情況就不再這麼簡單——直接做會導致係數亂套。考慮一個容斥:\(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;
}