• 考虑每一条哈密顿回路在所有竞赛图中的出现次数。
  • 发现如果确定一个环, 其他的边乱选就可以保证出现哈密顿回路。所以对于一条哈密顿回路, 出现次数为\(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;
}