[多项式][AGC005F] Many Easy Problems
拆贡献 + \(\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;
}

浙公网安备 33010602011771号