【LOJ 3124】「CTS 219」クリプトン手遊び


【タイトルリンク】
  • クリックしてリンク
  • を開く
    【考え方のポイント】
  • 与えられた図が外向ツリーである場合を考慮すると、各点はサブツリー内のすべての点よりも早く、s i z e i size_と記す必要がある.i sizeiはi i iサブツリー内のすべての点を表すw i w_i wiの和で受賞確率は∏i=1 Nw i s i z e iprod_{i=1}^{N}\frac{w_i}{size_i} i=1∏N​sizei​wi​​
  • 対すべてw i w_イwiの分布はリュックサックでOKです.
  • で与えられた図が外向ツリーでない場合、各逆エッジが無制限または順方向エッジであることを列挙し、反発原理で解決することができる.このプロセスは依然としてすべてのw i w_に対してイwiの分布はリュックサックを行います.
  • 時間複雑度O(N 2)O(N^2)O(N 2).

  • 【コード】
    #include
    using namespace std;
    const int MAXN = 3005;
    const int P = 998244353;
    typedef long long ll;
    typedef long double ld;
    typedef unsigned long long ull;
    template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
    template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
    template <typename T> void read(T &x) {
    	x = 0; int f = 1;
    	char c = getchar();
    	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
    	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
    	x *= f;
    }
    template <typename T> void write(T x) {
    	if (x < 0) x = -x, putchar('-');
    	if (x > 9) write(x / 10);
    	putchar(x % 10 + '0');
    }
    template <typename T> void writeln(T x) {
    	write(x);
    	puts("");
    }
    int power(int x, int y) {
    	if (y == 0) return 1;
    	int tmp = power(x, y / 2);
    	if (y % 2 == 0) return 1ll * tmp * tmp % P;
    	else return 1ll * tmp * tmp % P * x % P;
    }
    int n, p[MAXN][4], size[MAXN];
    int inv[MAXN], dp[MAXN][MAXN];
    vector <pair <int, bool>> a[MAXN];
    void update(int &x, int y) {
    	x += y;
    	if (x >= P) x -= P;
    }
    void work(int pos, int fa) {
    	for (auto x : a[pos])
    		if (x.first != fa) work(x.first, pos);
    	static int res[MAXN];
    	memset(res, 0, sizeof(res)), res[0] = 1;
    	for (auto x : a[pos])
    		if (x.first != fa) {
    			int dest = x.first;
    			if (!x.second) {
    				for (int i = 1; i <= size[dest]; i++) {
    					update(dp[dest][0], dp[dest][i]);
    					dp[dest][i] = (P - dp[dest][i]) % P;
    				}
    			}
    			static int tmp[MAXN];
    			for (int i = 0; i <= size[pos] + size[dest]; i++)
    				tmp[i] = 0;
    			for (int i = 0; i <= size[pos]; i++)
    			for (int j = 0; j <= size[dest]; j++)
    				update(tmp[i + j], 1ll * res[i] * dp[dest][j] % P);
    			for (int i = 0; i <= size[pos] + size[dest]; i++)
    				res[i] = tmp[i];
    			size[pos] += size[dest];
    		}
    	size[pos] += 3;
    	for (int i = 1; i <= size[pos]; i++) {
    		int tmp = 0;
    		for (int j = 1; j <= 3 && j <= i; j++)
    			update(tmp, 1ll * p[pos][j] * res[i - j] % P * j % P * inv[i] % P);
    		dp[pos][i] = tmp;
    	}
    }
    int main() {
    	read(n);
    	for (int i = 1; i <= n; i++) {
    		int x, y, z, s;
    		read(x), read(y), read(z);
    		s = power(x + y + z, P - 2);
    		p[i][1] = 1ll * x * s % P;
    		p[i][2] = 1ll * y * s % P;
    		p[i][3] = 1ll * z * s % P;
    	}
    	for (int i = 1; i <= n - 1; i++) {
    		int x, y; read(x), read(y);
    		a[x].emplace_back(y, true);
    		a[y].emplace_back(x, false);
    	}
    	for (int i = 1; i <= 3 * n; i++)
    		inv[i] = power(i, P - 2);
    	work(1, 0);
    	int ans = 0;
    	for (int i = 1; i <= size[1]; i++)
    		update(ans, dp[1][i]);
    	writeln(ans);
    	return 0;
    }