P7717 「EZEC-10」序列 (并查集 + dfs)
并查集 + 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\) 时:
- 如果 \(u\) 有两个儿子,那么继续往下两个儿子递归。
- 如果有一个儿子,那么这一位最大值有两种,一种为 \(0\),直接贡献 \(2^{dep}\) 方案数(之后的位随便选);一种为 \(1\),继续递归。
否则:
- 如果 \(u\) 有两个儿子,那么无解。
- 如有有一个儿子,那么继续递归。
复杂度 \(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;
}