题解 CF724G 【Xor-matic Number of the Graph】
在博客园食用更佳:https://www.cnblogs.com/PinkRabbit/p/CF724G.html。
题目传送门:CF724G。
题意简述:
一张
定义三元组
求所有合法三元组的
题解:
比较简单的线性基和图结合的题目,需要用到线性基的一些基本性质。
对异或线性基在图上的应用稍有了解的同学很快可以发现结论:
- 对于连通无向图
G=(V,E) 以及G 的一棵生成树T : -
- 点
u 到点v 所有路径的异或和可以被T 中u 到v 的路径的异或和异或上线性基B 表示出来。 - 更进一步地,
T 中u 到v 的路径的异或和等于u 到根的路径的异或和异或v 到根的路径的异或和。 - 所以
u 到v 所有路径的异或和等于d_u\oplus d_v\oplus B ,其中d_x 表示x 到根的路径的异或和。
对于一对
直接做并不是很好做,考虑按位分开做:
- 对于线性基
B 和二进制位w ,有结论: - 设
B 中元素个数为S ,则B 可以表示出2^S 个不同的数。 - 如果
B 中存在二进制第w 位为1 的数,则那2^S 个数中恰有2^{S-1} 个数的二进制第w 位为1 ,另外2^{S-1} 个数的二进制第w 位为0 。 - 如果
B 中不存在二进制第w 位为1 的数,显然不可能表示出二进制第w 位为1 的数,全部2^S 个数的二进制第w 位均为0 。
可以通过组合恒等式\sum_{i=0}^{n}\binom{n}{i}[i\bmod 2=1]=\begin{cases}0&,n=0\\2^{n-1}&,n>0\end{cases} 证明。
统计每一位有多少种能被表示出来的方式,统计进答案即可。
这样需要枚举
直接枚举二进制位
如果存在,这意味着无论
这意味着
如果不存在,这意味着
这意味着
最后注意原图不一定联通,对于每个联通块分别计算即可。
时间复杂度
#include <cstdio>
#include <cstring>
typedef long long LL;
const int Mod = 1000000007;
const int MN = 100005;
const int MM = 400005;
int N, M;
int h[MN], nxt[MM], to[MM], tot; LL w[MM];
inline void ins(int x, int y, LL z) { nxt[++tot] = h[x], to[tot] = y, w[tot] = z, h[x] = tot; }
LL B[60]; int C;
inline void Add(LL x) {
for (int j = 59; ~j; --j) if (x >> j & 1)
if (!B[j]) { B[j] = x, ++C; break; }
else x ^= B[j];
}
bool vis[MN];
LL d[MN];
int s[MN], t;
void DFS(int u, LL v) {
vis[u] = 1, d[u] = v, s[++t] = u;
for (int i = h[u]; i; i = nxt[i]) {
if (vis[to[i]]) Add(v ^ d[to[i]] ^ w[i]);
else DFS(to[i], v ^ w[i]);
}
}
LL Ans;
int main() {
scanf("%d%d", &N, &M);
for (int i = 1; i <= M; ++i) {
int x, y; LL z;
scanf("%d%d%lld", &x, &y, &z);
ins(x, y, z); ins(y, x, z);
}
for (int i = 1; i <= N; ++i) if (!vis[i]) {
memset(B, 0, sizeof B), C = t = 0;
DFS(i, 0);
for (int j = 0; j < 60; ++j) {
LL c = (1ll << j) % Mod;
bool ok = 0;
for (int k = 0; k < 60; ++k) if (B[k] >> j & 1) ok = 1;
if (ok) Ans = (Ans + (LL)t * (t - 1) / 2 % Mod * ((1ll << C - 1) % Mod) % Mod * c) % Mod;
else {
int x = 0;
for (int i = 1; i <= t; ++i) if (d[s[i]] >> j & 1) ++x;
Ans = (Ans + (LL)x * (t - x) % Mod * ((1ll << C) % Mod) % Mod * c) % Mod;
}
}
}
printf("%d\n", (LL)Ans % Mod);
return 0;
}