[多项式][AGC005F] Many Easy Problems

\(\texttt{link}\)

拆贡献 + \(\text{NTT}\)优化。

点集大小为 \(i\) 的集合有 \(\dbinom n i\) 个,考虑容斥求出每个点不在最小连通块的方案数。

观察一个点 \(u\) 不在最小连通块的充要条件:点集中的所有点都在 \(u\) 的某个子树内。(此时以点 \(u\) 为根)

写出来则有 \(f(i) = \sum\limits_{u=1}^n (\dbinom n i - \sum\limits_{v\in son(u)}\dbinom {size(v)}{i}) = n \dbinom n i - \sum\limits_{u=1}^n\sum\limits_{v\in son(u)}\dbinom{size(v)}{i}\)

后面的柿子换个写法,记 \(cnt(i)\) 表示大小为 \(i\) 的子树个数,则:

\[\begin{aligned}f(i) &= n \dbinom n i - \sum\limits_{j=i}^n cnt(j) \dbinom j i \\ &= n \dbinom n i - \sum\limits_{j=i}^n\dfrac{cnt(j)j!}{i!(j-i)!}\\&=n\dbinom n i - \dfrac 1 {i!}\sum\limits_{j=i}^ncnt(j)j!\dfrac 1 {(j-i)!}\end{aligned} \]

记后面一坨柿子为 \(g(i)\),继续推:

\[\begin{aligned}g(i) &= \dfrac 1 {i!}\sum\limits_{j=i}^ncnt(j)j!\dfrac 1 {(j-i)!} \\&= \dfrac 1 {i!}\sum\limits_{j=0}^{n-i}cnt(i+j)(i+j)!\dfrac 1 {j! }\end{aligned} \]

\(A(i)=cnt(i)i!, B(i) = \dfrac 1 {i!}\),套路地翻转 \(A\) 的系数,得到:

\[g(i) = \dfrac 1 {i!}\sum\limits_{j=0}^{n-i}A(n-i-j)B(j) \]

直接 \(\text{NTT}\) 卷起来即可。

[\(\texttt{Code:}\)]

#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define mp make_pair
#define fi first
#define se second

using namespace std;

inline void read(int &x) {
    x = 0; int f = 0; char c = getchar();
    while (!isdigit(c)) f |= c == '-', c = getchar();
    while (isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
    x = f ? -x : x;
}

const int cmd = 924844033;
const int N = 1e6 + 5;

int n, cnt[N], fac[N], ifac[N], h[N], f[N], g[N], s[N];

vector<int> G[N];

int fpow(int a, int b) {
    int res = 1;
    for (; b; b >>= 1, a = 1ll * a * a % cmd)
        if (b & 1) res = 1ll * res * a % cmd;
    return res;
}

int add(int a, int b) {a += b; return a < cmd ? a : a - cmd;}

int sub(int a, int b) {a -= b; return a < 0 ? a + cmd : a;}

void dfs(int u, int fa) {
	s[u] = 1;
	for (int v : G[u]) {
		if (v == fa) continue;
		dfs(v, u);
		s[u] += s[v];
		cnt[s[v]]++;
	}cnt[n - s[u]]++;
}

namespace Poly {
	const int L = (1 << 20) + 5;

	int lby, A[L], B[L], rev[L], yg = 5, invyg = fpow(5, cmd - 2);

	void init() {
		for (int i = 1; i < lby; i++)
			rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? lby >> 1 : 0);
	}

	void ntt(int *f, int tp) {
		for (int i = 1; i < lby; i++)
			if (i < rev[i]) swap(f[i], f[rev[i]]);
		for (int len = 2; len <= lby; len <<= 1) {
			int buf0 = fpow(tp ? yg : invyg, (cmd - 1) / len);
			for (int s = 0; s < lby; s += len) {
				int buf = 1;
				for (int i = 0; i < (len >> 1); i++) {
					int cur = 1ll * buf * f[s + (len >> 1) + i] % cmd;
					f[s + (len >> 1) + i] = sub(f[s + i], cur);
					f[s + i] = add(f[s + i], cur);
					buf = 1ll * buf * buf0 % cmd;
				}
			}
		}
	}

	void NTT(int *h, int *f, int *g) {
		memcpy(A, f, sizeof(int)*lby);
		memcpy(B, g, sizeof(int)*lby);
		init(); ntt(A, 1); ntt(B, 1);
		for (int i = 0; i < lby; i++) A[i] = 1ll * A[i] * B[i] % cmd;
		ntt(A, 0); int invl = fpow(lby, cmd - 2);
		for (int i = 0; i < lby; i++) h[i] = 1ll * A[i] * invl % cmd;
	}
}

using namespace Poly;

int C(int n, int m) {
	return 1ll * fac[n] * ifac[m] % cmd * ifac[n - m] % cmd;
}

int main() {
    // freopen("a.in", "r", stdin);
    // freopen("a.out", "w", stdout);
    read(n);
    for (int i = 1; i < n; i++) {
    	int u, v; read(u); read(v);
    	G[u].pb(v); G[v].pb(u);
    }
    dfs(1, 0);
    fac[0] = ifac[0] = 1;
    for (int i = 1; i <= n; i++) fac[i] = 1ll * fac[i - 1] * i % cmd;
    ifac[n] = fpow(fac[n], cmd - 2);
	for (int i = n - 1; i; i--) ifac[i] = 1ll * ifac[i + 1] * (i + 1) % cmd;
    for (int i = 1; i <= n; i++) f[i] = 1ll * cnt[i] * fac[i] % cmd;
    reverse(f, f + n + 1);
    for (int i = 0; i < n; i++) g[i] = ifac[i];
    // for (int i = 1; i <= n; i++) {
    // 	for (int j = 0; j <= n - i; j++)
    // 		h[n - i] = add(h[n - i], 1ll * f[n - i - j] * g[j] % cmd);
    // }
    lby = 1; for (; lby <= n + n; lby <<= 1);
	NTT(h, f, g);
	reverse(h, h + n + 1);
	for (int i = 1; i <= n; i++) {
		// int ans = 1ll * n * C(n, i) % cmd;
		// for (int j = i; j <= n; j++)
		// 	ans = sub(ans, 1ll * cnt[j] * C(j, i) % cmd);
		// printf("%d\n", ans);
		// printf("%lld\n", 1ll * ifac[i] * h[i] % cmd);
		printf("%d\n", sub(1ll * n * C(n, i) % cmd, 1ll * ifac[i] * h[i] % cmd));
	}
    return 0;
}
posted @ 2021-11-10 19:04  klii  阅读(45)  评论(0)    收藏  举报