带边数的无向连通图计数

就是求 \(n\) 个点 \(m\) 条边的带标号无向连通图个数。

首先可以用最暴力的 \(O(n^6)\) 做法,直接按城市规划一题的容斥 DP 做法,
\(f_{n,m}\) 表示答案,可以枚举 \(1\) 号点所在块的情况容斥计算。

\(O(n^4)\) 做法是一个有意思的斯特林反演。

考虑一个选了 \(m\) 条边的方案,且形成 \(k\) 个连通块方案块的方案是 \(F_{m,k}\)
现在我们还是 \(m\) 条边,但人为划分出来 \(j\) 个块一定不两两相连,但块内任意连边,方案是 \(G_{m,j}\)

有组合关系:

\[G_{m,k} = \sum_{j\ge k} S2(j, k) F_{m,j} \]

反演以后即:

\[F_{m,k} = \sum_{j\ge k} S1(j, k) (-1)^{j - k} G_{m,j} \]

我们要求的即:

\[F_{m,1} = \sum_{j\ge 1} (j - 1)! (-1)^{j - 1} G_{m,j} \]

考虑 DP 后面那个 \(G\),暴力大概不止 \(O(n^6)\)
但是我们发现算这个 \(G\) 只需要知道块的划分情况,那么这 \(m\) 条边的可以随意选取。
\(H_{i,j,m}\) 表示 DP 了 \(i\) 个点,分了 \(j\) 个块,当前共有 \(m\) 个边可以用的方案数。

则有:

\[G_{m,j}=\sum_{k\ge m} H_{n,j,k} {k\choose m} \]

直接 DP 这个 H 复杂度过高,接下来尝试缩去中间 \(j\) 那一维。
现在考虑把 \((j - 1)! (-1)^{j - 1}\) 的附加贡献在 DP 过程中算上,
发现我们可以先正常选一个块,并固定这个块的一号在最前面,
然后剩下的每加入一个块,带一个 \(-1\) 的系数并且把这个块的点任意插进去(只需保证不在开头,系数 \(i + k - 1\choose k\))进行 DP。
这样就做到让一个 \(k\) 个块的方案会算重 \((k - 1)!\) 次。
时间复杂度 \(O(n^4)\)

然而代码短小精悍。

void DP()
{
	for(int i = 1; i <= n; ++i)
		H[i][c2(i)] = 1;
	for(int i = 1; i <= n; ++i)
		for(int j = c2(i); j >= 0; --j)
			if(H[i][j])
				for(int k = 1; i + k <= n; ++k)
					H[i + k][j + c2(k)] = (H[i + k][j + c2(k)] - 1ll * H[i][j] * C[i + k - 1][k]) % mod;
	for(int j = 0; j <= c2(n); ++j)
	{
		for(int k = j; k <= c2(n); ++k)
			F[j] = (F[j] + 1ll * H[n][k] * C[k][j]) % mod;
		F[j] = (F[j] + mod) % mod;
		printf("%d\n", F[j]);
	}
}
posted @ 2020-06-05 21:41  bestwyj  阅读(1624)  评论(1编辑  收藏  举报