题解 P4233 【射命丸文的笔记】 && 考试T3
- 考虑每一条哈密顿回路在所有竞赛图中的出现次数。
- 发现如果确定一个环, 其他的边乱选就可以保证出现哈密顿回路。所以对于一条哈密顿回路, 出现次数为\(2^{C_n^2-n}\), 减去的\(n\)为那\(n\)条边。哈密顿回路是\(1-n\)的一个排列首尾拼在一起, 共有\(n!/n\)种。于是总贡献可以直接得出。
- 总贡献算好了, 问题转化为求有多少个存在哈密顿回路的竞赛图。
- 显然, 存在哈密顿回路的竞赛图一定强连通, 强连通的竞赛图一定有哈密顿回路。
- 所以问题就是求有多少个强连通的\(n\)个点的竞赛图。
- 设\(n\)个点的强连通的竞赛图个数为\(f_n\)。
- 考虑用所有图减去不是强连通的竞赛图, 可以得到:\[f_n =2^{C_n^2} - \sum_{i=1}^{n - 1}f_i\times C_n^i\times 2^{C_{n - i}^2} \]
- 这个式子的意思是枚举一个其他点无法到达的强连通图,也就是缩点之后拓扑序最小的强连通图,由于是任意的, 所以乘以组合数, 然后剩下的点数为\(n - i\)的图乱选。之所以可以这样是因为每个图一定会有一个最小的强连通图, 而且由于对于每个图我们只统计最小的, 所以做到不重不漏。
- 考虑如何计算\(f_i\)
- 设\(g_i=2^{C_i^2}\), 有:
\[f_n=g_n-\sum_{i=1}^{n-1}f_i\times C_n^i\times g_{n-i}
\]
\[g_n=\sum_{i=1}^nf_i\times C_n^i\times g_{n-i}
\]
\[\frac{g_n}{n!}=\sum_{i=1}^n\frac{f_i}{i!}\times \frac{g_{n-i}}{(n-i)!}
\]
- 设\(G_n=\frac{g_n}{n!}\), \(F_n=\frac{f_n}{n!}\)。
- 所以有:\[G_n = \sum_{i=1}^nF_iG_{n-i} \]
- 设\(A=\sum_{i = 0}^{inf}F_ix^i\), \(B=\sum_{i=0}^{inf}G_ix^i\), 然后有:
\[A * B=\sum_{i=0}^{inf}x^i\sum_{j+k=i}F_j\times G_k = B - G_0
\]
- 这里\(G_0 = 0\)
- 移项,多项式求逆就好了。
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
#define R register
const int N = 4e5 + 10;
const int P = 998244353;
const int G = 3;
inline int read() {
int x = 0, f = 1; char a = getchar();
for(; a > '9' || a < '0'; a = getchar()) if(a == '-') f = -1;
for(; a >= '0' && a <= '9'; a = getchar()) x = x * 10 + a - '0';
return x * f;
}
template <typename T>
inline int qpow(int x, T k) {
int res = 1;
while(k) {
if(k & 1) res = 1LL * res * x % P;
x = 1LL * x * x % P; k >>= 1;
} return res;
}
struct Poly {
int lim, rev[N];
inline void init(int Lim) {
lim = Lim;
for(R int i = 0; i < lim; i ++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? lim >> 1 : 0);
}
inline void NTT(int *A, int type = 1) {
for(R int i = 0; i < lim; i ++) if(i > rev[i]) swap(A[i], A[rev[i]]);
for(R int dep = 1; dep < lim; dep <<= 1) {
int Wn = qpow(type == 1 ? G : qpow(G, P - 2), (P - 1) / (dep << 1));
for(R int j = 0, len = dep << 1; j < lim; j += len) {
int w = 1;
for(R int k = 0; k < dep; k ++, w = 1LL * w * Wn % P) {
int x = A[j + k], y = 1LL * w * A[j + k + dep] % P;
A[j + k] = (x + y) % P;
A[j + k + dep] = (x - y + P) % P;
}
}
}
if(type == -1) {
int inv = qpow(lim, P - 2);
for(R int i = 0; i < lim; i ++) A[i] = 1LL * A[i] * inv % P;
}
}
int C[N];
inline void getinv(int dep, int *F, int *G) {
if(dep == 1) {
G[0] = qpow(F[0], P - 2); return ;
}
getinv((dep + 1) >> 1, F, G);
int lim = 1;
while(lim < (dep << 1)) lim <<= 1; init(lim);
for(R int i = 0; i < dep; i ++) C[i] = F[i];
for(R int i = dep + 1; i < lim; i ++) C[i] = 0;
NTT(C); NTT(G);
for(R int i = 0; i < lim; i ++)
G[i] = 1LL * (2 - 1LL * C[i] * G[i] % P + P) % P * G[i] % P;
NTT(G, -1);
for(R int i = dep; i < lim; i ++) G[i] = 0;
}
}T;
int n;
int f[N], g[N], F[N];
int fac[N], ifac[N];
int main() {
freopen("hamilton.in", "r", stdin);
freopen("hamilton.out", "w", stdout);
n = read(); fac[0] = 1;
if(n >= 1) cout << 1 << endl;
if(n >= 2) cout << -1 << endl;
for(R int i = 1; i <= n; i ++) fac[i] = 1LL * fac[i - 1] * i % P;
ifac[n] = qpow(fac[n], P - 2);
for(R int i = n - 1; i >= 0; i --) ifac[i] = 1LL * ifac[i + 1] * (i + 1) % P;
for(R int i = 0; i <= n; i ++) g[i] = 1LL * qpow(2, 1LL * i * (i - 1) / 2) * ifac[i] % P;
T.getinv(n + 1, g, F);
for(R int i = 0; i <= n; i ++) F[i] = (P - F[i]) % P;
for(R int i = 0; i <= n; i ++) f[i] = 1LL * F[i] * fac[i] % P;
for(R int i = 0; i <= n; i ++) g[i] = 1LL * g[i] * fac[i] % P;
for(R int i = 3; i <= n; i ++)
printf("%lld\n", 1LL * fac[i] * qpow(i, P - 2) % P * qpow(2, 1LL * i * (i - 1) / 2 - i) % P * qpow(f[i], P - 2) % P) ;
return 0;
}