节选自:线性代数学习笔记(二):线性基
我们先不考虑这道题是在图上操作,因此我们先来求不同异或和的和。
由于线性基中任意一组基异或起来都互不相同,这大大简化了这个问题,我们只需要统计即可,不需要考虑去重。
我们考虑按位算贡献。假设线性基中有 \(tot\) 个基,其中有 \(k (k > 0)\) 个基的第 \(i\) 位为 \(1\),那么就有 \(tot - k\) 个第 \(i\) 位为 \(0\) 的基。这些第 \(i\) 位为 \(0\) 的基无论选不选都对答案不造成贡献,因此最终这一位的答案一定会乘以 \(2^{tot - k}\)。
现在我们考虑第 \(i\) 位为 \(1\) 的 \(k\) 个基,只有当从中选出奇数个基时,才会在这一位上留下一个 \(1\),因此答案是 \(\displaystyle\sum_{0 \leq j \leq k \wedge j \bmod 2 = 1} \binom kj\)。
我们考虑二项式定理 \((x + y)^k = \displaystyle\sum_{0 \leq j \leq k} \binom kj x^j y^{k - j}\),由于 \(k > 0\),我们把 \(x, y\) 带成 \(-1\) 和 \(1\),那么 \(0 = \displaystyle\sum_{0 \leq j \leq k} \binom kj (-1)^j\),这个式子在 \(j\) 为偶数时为正,\(j\) 为奇数时为负,于是 \(\displaystyle\sum_{0 \leq j \leq k \wedge j \bmod 2 = 1} \binom kj = \sum_{0 \leq j \leq k \wedge j \bmod 2 = 0} \binom kj\)。
因为组合数一行之和 \(\displaystyle 2^k\),那么 \(\displaystyle\sum_{0 \leq j \leq k \wedge j \bmod 2 = 1} \binom kj = 2^{k - 1}\),于是这一位的答案就是 \(2^{tot - 1}\)。
很显然,如果 \(k\) 为 \(0\) 那么答案就是 \(0\),因此如果存在一个最高位为 \(i\) 的基,那么答案就会乘以 \(2^{tot - 1}\),那么答案就是 \(\displaystyle\prod_{i = 0}^{\log n} 2^{tot - 1} [w_i \neq 0]\)。
回到这道题目,我们需要求不同的路径异或和的和。参考例题十一,我们把所有环插入线性基中,再找到一条主路径,那么最终答案就是线性基中的答案异或上主路径长度,但 \(u\) 和 \(v\) 都是不固定的,如果一对一对枚举,复杂度直接炸了,考虑如何优化。
还是考虑异或相等为 \(0\) 的情况。如果我们随便找一个点作为起点,记录到 \(u\) 的距离 \(dis_u\) 和到 \(v\) 的距离 \(dis_v\),那么 \(u\) 到 \(v\) 的距离就是 \(dis_u \operatorname{xor} dis_v\)。
那么现在问题就变成了在集合 \(\{dis_i\}\),中任意选两个数字 \(dis_u, dis_v\),我们求出线性基中异或和的和,再异或上 \(dis_u\) 和 \(dis_v\),加入到答案中就行了。但这样复杂度依然爆炸。
我们继续考虑按位贡献,我们知道线性基如果有一个数第 \(i\) 位上是 \(1\),那么这一位上就有 \(2^{tot - 1}\) 种方式凑出 \(1\),\(2^{tot - 1}\) 种方式凑出 \(0\)。因此无论 \(\{dis_i\}\) 中选出两个数的异或和的第 \(i\) 位为 \(1\) 或者 \(0\),对答案的贡献都是 \(2^{tot - 1}\)。假设 \(\{dis_i\}\) 的大小为 \(siz\),那么总贡献就是 \(2^{tot - 1} \times \displaystyle\frac{siz(siz - 1)}{2}\)。
如果线性基中没有数字第 \(i\) 位上是 \(1\),那么无论多少个数异或起来都是 \(0\),因此答案需要乘以 \(2^{tot}\)。其次选出的 \(dis_u\) 和 \(dis_v\) 的第 \(i\) 位上必须一个是 \(0\),一个是 \(1\)。我们假设 \(\{dis_i\}\) 中有 \(cnt\) 个数字第 \(i\) 位上为 \(1\),那么对于答案的贡献就是 \(2^{tot} \times cnt \times (siz - cnt)\)。
最后,这道题的图可能不联通,每个连通块分开算即可。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 9, M = 2e5 + 9, MOD = 1e9 + 7;
struct Edge{int v, w, nex;
} e[M << 1];
int head[N], ecnt;
void addEdge(int u, int v, int w){e[++ecnt] = Edge{v, w, head[u]};head[u] = ecnt;
}
int qpow(int a, int b){int res = 1;while(b > 0){if(b & 1)res = res * a % MOD;a = a * a % MOD;b >>= 1;}return res;
}
int dis[N], n, m, ans, sum;
vector <int> vec;
bool vis[N];
struct Basis{int w[69], tot;void clear(){memset(w, 0, sizeof(w));tot = 0;}void insert(int x){for(int i = 62; ~i; i--){if(x >> i & 1ll){if(w[i])x ^= w[i];else {w[i] = x;tot++;break;}}}}int calc(){int sum = 0, res = 0, siz = vec.size();for(int i = 62; ~i; i--)sum |= w[i];for(int i = 62; ~i; i--){int cnt = 0;for(int j = 0; j < siz; j++)if(vec[j] >> i & 1ll)cnt++;if(sum >> i & 1ll)res = (res + qpow(2, i) % MOD * qpow(2, tot - 1) % MOD * (siz * (siz - 1) / 2 % MOD) % MOD) % MOD;elseres = (res + qpow(2, i) % MOD * qpow(2, tot) % MOD * cnt % MOD * (siz - cnt) % MOD) % MOD;}return res;}
} b;
void dfs(int u){vis[u] = true;vec.push_back(dis[u]);for(int i = head[u]; i; i = e[i].nex){int v = e[i].v;if(vis[v])b.insert(dis[u] ^ dis[v] ^ e[i].w);else {dis[v] = dis[u] ^ e[i].w;dfs(v);}}
}
signed main(){scanf("%lld%lld", &n, &m);for(int i = 1; i <= m; i++){int u, v, w;scanf("%lld%lld%lld", &u, &v, &w);addEdge(u, v, w);addEdge(v, u, w);}for(int i = 1; i <= n; i++){if(!vis[i]){b.clear();vec.clear();dfs(i);ans = (ans + b.calc()) % MOD;}}printf("%lld", ans);return 0;
}