题解:P16930 拱辰封仪

难点在于会 NTT。

尝试刻画 \(x,y\) 之间存在长度为奇数的路径的条件。显然 \(x,y\) 要在同一个连通块中。注意到若连通块中存在奇环,则任取一条路径走奇环即可改变其长度奇偶性,因此一定存在长度为奇数的路径;若连通块为二分图,则存在长度为奇数的路径当且仅当 \(x,y\) 在二染色后异色。

\(E^2-O^2\) 转化成求 \((E+O)(E-O)\)

先求 \(E+O\),也就是求总方案数。对每个连通块构造多项式:

  • 若连通块不为二分图,设共有 \(c\) 个点,构造多项式 \(1+cx\)
  • 若连通块为二分图,设二染色后有 \(c_0\) 个左部点,\(c_1\) 个右部点,构造多项式 \((1+x)^{c_0}+(1+x)^{c_1}-1\)

把每个连通块对应的多项式分治乘起来即可。

再求 \(E-O\),相当于求所有方案的 \((-1)^{\sum a_i}\) 之和。类似地对每个连通块构造多项式:

  • 若连通块不为二分图,设有 \(c_0\) 个点点权为偶数,\(c_1\) 个点点权为奇数,构造多项式 \(1+(c_0-c_1)x\)
  • 若连通块为二分图,设二染色后,左部点中点权为偶数/奇数的点有 \(c_{0,0/1}\) 个,右部点中点权为偶数/奇数的点有 \(c_{1,0/1}\) 个,构造多项式 \((1+x)^{c_{0,0}}(1-x)^{c_{0,1}}+(1+x)^{c_{1,0}}(1-x)^{c_{1,1}}-1\)

同样分治乘即可。

时间复杂度为 \(\mathcal{O}(n\log^2{n})\)

主要代码
poly binom1(int n) {
	poly H(n + 1);
	for (int i = 0; i <= n; ++i) H[i] = C(n, i);
	return H;
}

poly binom2(int n) {
	poly H(n + 1);
	for (int i = 0; i <= n; ++i) H[i] = C(n, i) * (i & 1 ? -1 : 1);
	return H;
}

poly solve(const vector<poly> &vec, int l, int r) {
	if (l == r) return vec[l];
	int mid = l + r >> 1;
	return mul(solve(vec, l, mid), solve(vec, mid + 1, r));
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cin >> n >> m >> k;
	init(n);
	for (int i = 1; i <= n; ++i) cin >> a[i];
	for (int i = 1; i <= m; ++i) {
		int u, v;
		cin >> u >> v;
		G[u].emplace_back(v);
		G[v].emplace_back(u);
	}

	fill(c + 1, c + n + 1, -1);
	int id = 0;
	for (int x = 1; x <= n; ++x) {
		if (c[x] != -1) continue;

		++id;
		bool suc = true;
		array<int, 2> cntCol;
		array<array<int, 2>, 2> cntVal;
		cntCol.fill(0);
		for (auto &arr : cntVal) arr.fill(0);

		auto dfs = [&](auto &&self, int u) -> void {
			++cntCol[c[u]];
			++cntVal[c[u]][a[u] & 1];
			for (auto v : G[u]) {
				if (c[v] == -1) {
					c[v] = c[u] ^ 1;
					self(self, v);
				} else if (c[u] == c[v]) {
					suc = false;
				}
			}
		};

		c[x] = 0;
		dfs(dfs, x);

		poly p1, p2;
		if (!suc) {
			p1 = {1, cntCol[0] + cntCol[1]};
			p2 = {1, cntVal[0][0] + cntVal[1][0] - cntVal[0][1] - cntVal[1][1]};
		} else {
			p1 = add(binom1(cntCol[0]), binom1(cntCol[1]));
			p1[0] -= 1;
			p2 = add(
				mul(binom1(cntVal[0][0]), binom2(cntVal[0][1])),
				mul(binom1(cntVal[1][0]), binom2(cntVal[1][1]))
			);
			p2[0] -= 1;
		}
		F1.emplace_back(p1);
		F2.emplace_back(p2);
	}
	auto H1 = solve(F1, 0, F1.size() - 1);
	auto H2 = solve(F2, 0, F2.size() - 1);
	mint val1 = k < H1.size() ? H1[k] : 0, val2 = k < H2.size() ? H2[k] : 0;
	cout << val1 * val2;
	return 0;
}
posted @ 2026-06-18 17:39  P2441M  阅读(5)  评论(0)    收藏  举报