奇偶树

题面

有一棵 $n$ 个节点的树,节点编号为 $1\sim n$ 。树的边权的取值只有 $0$ $1$ ,且应当满足 $Q$ 个条件。每个条件形如 $(u,v,x)$ ,其中 $u$ $v$ 是树中节点的编号, $x$ $0$ $1$

条件的含义为树上从 $u$ $v$ 的路径上的边权和在模 $2$ 意义下应与 $x$ 相等(即若 $x=0$ 则和应为偶数;若 $x=1$ 则和应为奇数)。

满足这 $Q$ 个条件的前提下,整棵树的边权有多少种方案。由于答案可能很大,请将方案数对 取模后输出。

题解

注意模 2 下的加法为异或。对于每个点,设 $d_x$ 表示从根节点到它的异或和,则一个条件 $(u,v,x)$ 即为 $d_u\oplus d_v=x$ ,其中 $\oplus$ 表示异或。那么用带权并查集维护即可。

代码

const int N = 1e5 + 5, mod = 1e9 + 7;

inline ll Read() {
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

namespace Main {
	int n, m;
	int fa[N], d[N];
	int find (int k) {
		if (fa[k] == k) return k;
		int t = fa[k];
		fa[k] = find(fa[k]);
		d[k] ^= d[t];
		return fa[k];
	}
	int qpow (int a, int b) {
		int ans = 1;
		for (; b; b >>= 1, a = 1ll * a * a % mod)
			if (b & 1) ans = 1ll * ans * a % mod;
		return ans;
	}
	int main () {
		n = Read(), m = Read();
		for (int i = 1; i < n; i++) int u = Read(), v = Read();
		for (int i = 1; i <= n; i++) fa[i] = i;
		for (int i = 1; i <= m; i++) {
			int u = Read(), v = Read(), w = Read();
			int ru = find(u), rv = find(v);
			if (ru == rv && d[u] != (d[v] ^ w)) {
				puts("0"); return 0;
			} else if (ru != rv) {
				fa[ru] = rv;
				d[ru] = d[u] ^ d[v] ^ w;
			}
		}
		int cnt = 0;
		for (int i = 1; i <= n; i++) if (find(i) == i) cnt++;
		printf ("%d\n", qpow (2, cnt - 1));
		return 0;
	}
}

int main () {
	freopen("tree.in", "r", stdin);
	freopen("tree.out", "w", stdout);
	Main::main();
	return 0;
}
EOF

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。

博客信息

作者
Jayun
时间
2023-10-18 09:25:56