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

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


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

(n2^kleq 10^6),答案对 (998244353) 取模。

sol.

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

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

(k) 足够大的时候,这其实给出了一个优于传统斯特林容斥的 (mathcal O(n^3log 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);
}
原文地址:https://www.cnblogs.com/whx1003/p/15331253.html