无向连通图计数的一些想法

前两天遇到了一个题,机房老哥打算把它出成对答案的计数,中间有一步需要求恰有奇数条边的连通图个数。然后就有人提出推广到对每个 \(0\leq i<k\)\(\bmod k\equiv i\) 的个数,大致编出来一个 \(\mathcal O(nk\log n)\) 的做法。


Problem. 给出正整数 \(n,k\),请对每个 \(0\leq i<2^k\) 求出有多少个 \(n\) 个点的带标号无向连通图满足边数 \(\bmod 2^k\equiv i\)

\(n2^k\leq 10^6\),答案对 \(998244353\) 取模。

sol.

考虑记 \(f_{i,j}\)\(i\) 个点 \(j\) 条边的无向图个数,\(g_{i,j}\)\(i\) 个点 \(j\) 条边的无向连通图个数。考虑其生成函数,\(F\) 其实是 \(G\)\(x\) 一维上做 EGF 卷积,\(y\) 一维上做 OGF 卷积的结果。记 \(\tilde F=\sum_{i,j}f_{i,j}\frac{x^iy^j}{i!},\tilde G=\sum_{i,j}g_{i,j}\frac{x^iy^j}{i!}\),那么实际上有 \(\tilde F=\exp\tilde G\)

那么我们现在要求的就是 \([x^n]\ln \tilde F\bmod (y^{2^k}-1)\)。注意到那个模实际上是循环卷积,于是等价于对 \(y\) 一维做长为 \(2^k\) 的 FFT,然后求 \(\ln\),最后再 FFT 回来。直接暴力模拟这个过程即可做到 \(\mathcal O(n2^k\log n)\)

\(k\) 足够大的时候,这其实给出了一个优于传统斯特林容斥的 \(\mathcal O(n^3\log n)\) 的做法,必可活用于下一次

#include "poly.h"

int n, k;
int main() {
	n = getll(), k = getll();

	std::vector<poly> F(1 << k);
	for(int i = 0; i < (1 << k); ++i) F[i].redeg(n);
	
	ll w = fsp(gen, mod - 1 >> k);
	for(int i = 0; i <= n; ++i) {
		poly G; G.redeg((1 << k) - 1);
		
		ll cur = 1;
		for(int j = 0; j < (1 << k); ++j)
			G[j] = fsp(1 + cur, 1ll * i * (i - 1) / 2), cur = cur * w % mod;
		
		for(int j = 0; j < (1 << k); ++j) F[j][i] = G[j];
	}
	
	poly ans; ans.redeg((1 << k) - 1);
	for(int i = 0; i < (1 << k); ++i) {
		for(int j = 0; j <= n; ++j) F[i][j] = F[i][j] * Math::Inv(j) % mod;
		ans[i] = F[i].ln()[n] * Math::Fac(n) % mod;
	}
	Poly::NTT(ans, k, -1), ans.print((1 << k) - 1);
}
posted @ 2021-09-24 17:06  whx1003  阅读(215)  评论(0编辑  收藏  举报