题解:P17781 千早爱音不是罪过

比较套路。

使用子集反演:设保留边集合为 \(S\),钦定 \(T\subseteq S\) 中的边保留,容斥系数为 \((-1)^{|S|-|T|}\)。因此问题可以转化为,给一部分边染成黑色或者白色,权值为将黑边保留后各连通块的点权异或和之积,乘上 \(-1\) 的白边数次方,求所有染色方案的权值和。

显然每个点至多向一个儿子连一条染色边,所有染色边形成若干条链,我们只需要把这些链缩起来再做拓扑序计数。

对于 \(u\) 子树内的一种染色方案,设将染色边缩起来后有 \(i\) 个点,有 \(c\) 种拓扑序,包含 \(u\) 的黑边连通块的异或和为 \(s\),其他黑边连通块的异或和之积乘上容斥系数为 \(w\)。设计 DP:令 \(f_{u,i}\) 表示 \(\sum wcs\),\(g_{u,i,k}\) 表示 \(\sum wc\operatorname{bit}_k(s)\),\(h_{u,i}\) 表示 \(\sum wc\),合并儿子节点时令 \(P_i\) 表示 \(h_{u,i}\) 中 \(u\) 没有向儿子节点连染色边的权值和。

考虑转移。设 \(u\) 所在部分缩点后有 \(i\) 个点,\(v\) 子树内有 \(j\) 个点。若 \((u,v)\) 不染色,则合并后有 \(i+j\) 个点,拓扑序方案数需要乘上 \(\dbinom{i+j-1}{j}\):

\[\begin{align*} g'_{u,i+j,k}&\gets g_{u,i,k}f_{v,j}\dbinom{i+j-1}{j}\\ h'_{u,i+j}&\gets h_{u,i}f_{v,j}\dbinom{i+j-1}{j}\\ P'_{i+j}&\gets P_if_{v,j}\dbinom{i+j-1}{j}\\ \end{align*} \]

若 \((u,v)\) 染色,则 \(u\) 所在部分必须拿 \(P_i\) 来贡献。合并后有 \(i+j-1\) 个点,拓扑序方案数需要乘上 \(\dbinom{i+j-2}{j-1}\)。

对于 \(h\),若 \((u,v)\) 染成黑色则两个连通块合并,贡献 \(h_{v,j}\),若染成白色则贡献 \(-f_{v,j}\):

\[h'_{u,i+j-1}\gets P_i(h_{v,j}-f_{v,j})\dbinom{i+j-2}{j-1} \]

对于 \(g\),分讨 \(a_u\) 第 \(k\) 位的取值:

  • 若第 \(k\) 位为 \(0\),将 \((u,v)\) 染成黑色会使这一位取 \(v\) 所在连通块的对应位,染成白色没有贡献:

    \[g'_{u,i+j-1,k}\gets P_ig_{v,j,k}\dbinom{i+j-2}{j-1} \]

  • 若第 \(k\) 位为 \(1\),将 \((u,v)\) 染成黑色时会翻转 \(v\) 所在连通块的对应位,贡献为 \(h_{v,j}-g_{v,j,k}\),染成白色时贡献为 \(-f_{v,j}\):

    \[g'_{u,i+j-1,k}\gets P_i(h_{v,j}-g_{v,j,k}-f_{v,j})\dbinom{i+j-2}{j-1} \]

合并完所有儿子后,令 \(f_{u,i}=\sum_k g_{u,i,k}2^k\) 即可。

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

主要代码
int n;
ull a[MAXN];
vector<int> T[MAXN];
mint C[MAXN][MAXN];
vector<mint> f[MAXN], h[MAXN];
vector<array<mint, 64>> g[MAXN];

void init(int n) {
	for (int i = 0; i <= n; ++i) {
		for (int j = 0; j <= i; ++j) {
			C[i][j] = !j ? 1 : C[i - 1][j - 1] + C[i - 1][j];
		}
	}
}

void dfs(int u, int fu) {
	vector<mint> p(2);
	h[u].resize(2);
	g[u].resize(2);
	p[1] = h[u][1] = 1;
	for (int k = 0; k < 64; ++k) g[u][1][k] = a[u] >> k & 1;
	
	int szu = 1;
	for (int v : T[u]) {
		if (v == fu) continue;
		dfs(v, u);
		int szv = f[v].size() - 1;
		vector<mint> nh(szu + szv + 1), np(szu + szv + 1);
		vector<array<mint, 64>> ng(szu + szv + 1);
		for (int i = 1; i <= szu; ++i) {
			for (int j = 1; j <= szv; ++j) {
				np[i + j] += p[i] * f[v][j] * C[i + j - 1][j];
				nh[i + j] += h[u][i] * f[v][j] * C[i + j - 1][j];
				nh[i + j - 1] += p[i] * (h[v][j] - f[v][j]) * C[i + j - 2][j - 1];
				for (int k = 0; k < 64; ++k) {
					ng[i + j][k] += g[u][i][k] * f[v][j] * C[i + j - 1][j];
					if (a[u] >> k & 1) ng[i + j - 1][k] += p[i] * (h[v][j] - g[v][j][k] - f[v][j]) * C[i + j - 2][j - 1];
					else ng[i + j - 1][k] += p[i] * g[v][j][k] * C[i + j - 2][j - 1];
				}
			}
		}

		nh.swap(h[u]);
		np.swap(p);
		ng.swap(g[u]);
		szu += szv;
	}

	f[u].resize(szu + 1);
	for (int i = 1; i <= szu; ++i) {
		for (int k = 0; k < 64; ++k) {
			f[u][i] += g[u][i][k] * (1ull << k);
		}
	}
}

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

	init(n);
	dfs(1, 0);

	mint ans = 0;
	for (int i = 1; i <= n; ++i) ans += f[1][i];
	cout << ans;
	return 0;
}
posted @ 2026-10-25 13:12  P2441M  阅读(26)  评论(2)    收藏  举报