首先题目说了:保证 G 中不存在简单环使得边权异或和不为 0。
即保证G若存在环,则环之间的边权异或和为0.因此走环相当于异或了个0,即走不走环都一样所以我们可以不走环,
即建立一条最小生成树然后计算它每个子节点n与根节点1的异或和dist[n],最后若要求a, b之间的异或值即dist[a] ^ dist[b]这个式子表示a与b到根节点公共的部分(最近公共祖先)异或了2次,我们知道同一个数异或两次相当于没执行这个操作,所以a, b之间的异或和为dist[a] ^ dist[b]
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int N = 100010, M = 4 * N; int p[N]; int dist[N];//子节点到根节点异或值 int n, m, q; int h[N], e[M], ne[M], w[M], idx;//邻接表数组 struct Edge { int a, b, c; }edges[2 * N]; int find(int x)//并查集模板 { if (p[x] != x) p[x] = find(p[x]); return p[x]; } void add(int a, int b, int c)//邻接表模板 { e[idx] = b; w[idx] = c; ne[idx] = h[a]; h[a] = idx; idx ++ ; } void kruskal()//最小生成树模板 { for (int i = 1; i <= n; i ++ ) p[i] = i; for (int i = 1; i <= m; i ++ ) { int a = edges[i].a, b = edges[i].b, c = edges[i].c; int pa = find(a), pb = find(b); if (pa != pb) { p[pa] = pb; add(a, b, c); add(b, a, c); } } } void dfs(int u, int fa)//计算子节点与根节点异或和 { for (int i = h[u]; i != -1; i = ne[i]) { int j = e[i]; if (j == fa) continue; dist[j] = dist[u] ^ w[i]; dfs(j, u); } } int main() { memset(h, -1, sizeof h); cin >> n >> m >> q; for (int i = 1; i <= m; i ++ ) { int a, b, c; scanf("%d %d %d", &a, &b, &c); edges[i] = {a, b, c}; } kruskal(); dfs(1, -1); while (q -- ) { int a, b; scanf("%d %d", &a, &b); printf("%d\n", dist[a] ^ dist[b]); } return 0; }