Loading

P7717 「EZEC-10」序列 (并查集 + dfs)

P7717 「EZEC-10」序列

并查集 + dfs

题目的限制容易想到并查集维护异或和的经典做法,由于异或和的传递性,可以维护 \(d_x\) 表示 \(x\) 到联通块根的异或和,同时判断新的限制是否满足条件。

不同连通块相互独立,满足乘法原理。在同一个连通块内,确定一个值,其他值也确定(根据 \(a_x⊕d_x=rt\))。考虑确定连通块根的值,那么题目就转化成:有多少种取值 \(rt\),满足连通块内所有 \(d_x⊕rt\le k\)

考虑建 01 trie,那么从大到小考虑方案数,显然限制只关心当前 \(\max(d_x⊕rt)\) 是否超过 \(k\),可以设函数 \(f(u,dep)\) 表示当前在 trie 树上 \(u\) 节点,考虑到二进制 \(dep\) 位的方案数,递归最大值求解:

\(k\) 这一位为 \(1\) 时:

  1. 如果 \(u\) 有两个儿子,那么继续往下两个儿子递归。
  2. 如果有一个儿子,那么这一位最大值有两种,一种为 \(0\),直接贡献 \(2^{dep}\) 方案数(之后的位随便选);一种为 \(1\),继续递归。

否则:

  1. 如果 \(u\) 有两个儿子,那么无解。
  2. 如有有一个儿子,那么继续递归。

复杂度 \(O(n\log k)\)

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 5e5 + 10, mod = 1e9 + 7;
bool flg;
i64 n, m, k, ans = 1, ret, tot;
i64 fa[N], d[N];
std::vector<int> v[N];
int tr[N * 30][2];
int find(int x) {
	if(x == fa[x]) return x;
	int rt = find(fa[x]);
	d[x] ^= d[fa[x]];
	return fa[x] = rt;
}
void merge(int x, int y, i64 z) {
	int fx = find(x), fy = find(y);
	if(fx == fy) {		
		if(d[x] ^ d[y] ^ z) flg = 1;
	} else {
		fa[fx] = fy;
		d[fx] ^= z ^ d[x] ^ d[y]; //为什么这样写?
	}
}

i64 qpow(i64 a, i64 b) {
	i64 ret = 1;
	while(b) {
		if(b & 1) ret = ret * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return ret;
}

void init() {
	for(int i = 0; i <= tot; i++) tr[i][0] = tr[i][1] = 0;
	ret = tot = 0;
}
void insert(i64 x) {
	int u = 0;
	for(int i = 29; i >= 0; i--) {
		int c = (x >> i) & 1;
		if(!tr[u][c]) tr[u][c] = ++tot;
		u = tr[u][c];
	}
}
i64 dfs(int u, i64 dep) {
	if(dep == -1) {
		return 1;
	}
	int vis0 = tr[u][0], vis1 = tr[u][1];
	if((k >> dep) & 1LL) {
		if(vis0 && vis1) {
			return (dfs(tr[u][0], dep - 1) + dfs(tr[u][1], dep - 1)) % mod;
		} else if(vis0) {
			ret = (ret + qpow(2, dep)) % mod;
			return (qpow(2, dep) + dfs(tr[u][0], dep - 1)) % mod;
		} else if(vis1) {
			ret = (ret + qpow(2, dep)) % mod;
			return (qpow(2, dep) + dfs(tr[u][1], dep - 1)) % mod;
		} 
	} else {
		if(vis0 && vis1) return 0;
		else if(vis0) return dfs(tr[u][0], dep - 1);
		else if(vis1) return dfs(tr[u][1], dep - 1);
	}
	return 0;
}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> n >> m >> k;

	for(int i = 1; i <= n; i++) fa[i] = i;

	for(int i = 1; i <= m; i++) {
		i64 x, y, z;
		std::cin >> x >> y >> z;
		merge(x, y, z);
	}

	if(flg) {
		std::cout << "0\n";
		return 0;
	}

	for(int i = 1; i <= n; i++) {
		int f = find(i);
		v[f].pb(i);
	}

	for(int i = 1; i <= n; i++) {
		if(v[i].size()) {
			init();
			for(auto x : v[i]) { 
			 	insert(d[x]); 
			} 
			ans = ans * dfs(0, 29) % mod; 
		}
	}

	std::cout << ans << "\n";

	return 0;
}
posted @ 2024-06-29 20:44  Fire_Raku  阅读(11)  评论(0编辑  收藏  举报