http://acm.hdu.edu.cn/showproblem.php?pid=5544
题意:
有一个n(<=50000)个顶点m(<=100000)条边的无向图,每条边有一个边权(0<=边权<2^60),求所有回路中边权xor和的最大值。
思路:
任意选定一个起点,从起点出发开始dfs,同时记录路径xor和,每访问到一个访问过的点表明得到了一个环,将当前异或值与起点到当前点的xor和xor一下可以得到环上的xor和,由于xor的性质,任意一个回路都可以用前面得到的环来线性表示,于是对之前得到的xor和做一次xor高斯消元,得到一组向量基,令ans=0,枚举每个向量基,用ans=max(ans,ans^base)更新答案即可,复杂度O(60*(n+m))。
这题其实重要的就是抑或高斯消元,我们最终想把每个权值都用二进制表示,每一个基代表相应的向量的最高位是那个且是1,这样最后我们就可以贪心的把每一位变成1,而不用考虑其余低位的情况(因为二进制的性质)!
这时我们需要了解一些性质:
假设集合A,其中我们将ai替换成ai xor a[j] i<>j,那么得到的新集合,这个集合中元素能xor出的值于集合A相同,而此时我们恰恰需要从高到低,把其余对应位是1的异或为0,此时无影响!
#include <bits/stdc++.h>
#define LL long long
#define FOR(i,x,y) for(int i = x;i < y;++ i)
#define IFOR(i,x,y) for(int i = x;i > y;-- i)
using namespace std;
const int maxn = 50050;
const int maxm = 100010;
int head[maxn],edge_cnt,n,m,tot;
struct Edge{
int u,v;
LL val;
int nt;
}edge[maxm<<1];
void addedge(int u,int v,LL val){
edge[edge_cnt].u = u;
edge[edge_cnt].v = v;
edge[edge_cnt].val = val;
edge[edge_cnt].nt = head[u];
head[u] = edge_cnt++;
}
void Build_Graph(){
memset(head,-1,sizeof(head));
edge_cnt = 0;
scanf("%d%d",&n,&m);
FOR(i,0,m){
int u,v; LL val;
scanf("%d%d%I64d",&u,&v,&val);
addedge(u,v,val);
addedge(v,u,val);
}
}
LL val[maxm<<1];
LL d[maxn];
bool vis[maxn];
void dfs(int u,int fa){
vis[u] = true;
for(int i = head[u];i != -1;i = edge[i].nt){
int v = edge[i].v;
if(v == fa) continue;
if(!vis[v]){
d[v] = d[u] ^ edge[i].val;
dfs(v,u);
}
else{
val[tot++] = d[u]^d[v]^edge[i].val;
}
}
}
int xorguass(int n){
int row = 0;
for(int i = 60;i >= 0;-- i){
int j;
for(j = row;j < n;++ j){
if(val[j] & (1LL<<i)) break;
}
if(j != n){
swap(val[j],val[row]);
for(j = 0;j < n;++ j){
if(j == row) continue;
if(val[j] & (1LL<<i)) val[j] ^= val[row];
}
++ row;
}
}
return row;
}
void work(){
memset(vis,false,sizeof(vis));
tot = 0;
d[1] = 0;
dfs(1,-1);
n = xorguass(tot);
LL ans = 0;
FOR(i,0,n) ans ^= val[i];
printf("%I64d\n",ans);
}
int main()
{
int T,tCase = 0; scanf("%d",&T);
while(T--){
printf("Case #%d: ",++tCase);
Build_Graph();
work();
}
return 0;
}