BZOJ 2115 异或线性基+DFS找环

来源:互联网 发布:linux下多线程编程 编辑:程序博客网 时间:2024/06/14 08:23

题目链接


题意:给定一个n个点,m条边的无向图,求出一条1>n节点的路径,使得路径上经过的边的权值的异或和最大。

思路:
解决此题只需要基于一个事实:
任意一条1>n的路径的异或和都可以由任意一条1>n的路径和与图中一些环的异或和组合而成。

为什么呢?

简单画个图,举个例子~:

考虑下面的这个图,n=8 m=9,存在3个环,标号123

这里写图片描述

可见从1>8有3条路径。
对于任意的一条,比如说:1>5>6>7>8
当该路径异或上标号2这个环:1>5>6>7>4>2>1

易发现,路径1>5>6>7异或上了两次,故直接抵消,剩下的路径异或为:
1>2>4>7>8
刚好为第二条路径。

同理,当该路径异或上标号3这个环,就得到了最下面的1>n的路径。

故易发现:对于一个经过某路径的环来说,一定是从某个点u出发形成两条路径,最后两条路径又一定会在一个点v相聚,故异或上一个环,就相当于对于u>v这个过程,走另外的一条路。

那没有经过路径的环呢?比如1>5>6>7>8和环1
对于这种情况,我们可以看成我们先到那个环走上一圈,再原路返回,这样从起点到环的路径走了两次,异或后为0,剩下的异或和就只剩环的异或和。

对于上面的情况,异或上环1就相当于我们从1出发,绕环1一圈,回到1之后再走路径1>5>6>7>8到达终点。


故我们可以找出图中的所有的环的异或和,构建其异或线性基,然后对于一条任意的从1>n的路径异或和dis[n],从大到小枚举线性基,若能使dis[n]增大,则加上其贡献。

至此,大功告成。

代码:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;typedef long long ll;const int A = 5e4 + 10;class Gra{public:    int v,next;ll w;}G[A<<2];int head[A],tot,twt,n,m;ll dis[A],a[A<<2],b[110];bool vis[A];void add(int u,int v,ll w){    G[tot].v = v;    G[tot].w = w;    G[tot].next = head[u];    head[u] = tot++;}void dfs(int u,int pre){    vis[u] = 1;    for(int i=head[u] ;i!=-1 ;i=G[i].next){        int v = G[i].v;ll w = G[i].w;        if(v == pre) continue;        if(!vis[v]){            dis[v] = dis[u] ^ w;            dfs(v,u);        }        else a[++twt] = dis[u]^dis[v]^w;    }}void init_Xor(){    for(int i=1 ;i<=twt ;i++) for(int j=62 ;j>=0 ;j--){        if(a[i]>>j & 1){            if(b[j]) a[i] ^= b[j];            else{                b[j] = a[i];                for(int k=j-1 ;k>=0 ;k--) if(b[k] && (b[j]>>k&1)) b[j] ^= b[k];                for(int k=j+1 ;k<=62;k++) if(b[k]>>j&1)           b[k] ^= b[j];                break;            }        }    }}int main(){    memset(head,-1,sizeof(head));    scanf("%d%d",&n,&m);    for(int i=1 ;i<=m ;i++){        int u,v;ll w;        scanf("%d%d%lld",&u,&v,&w);        add(u,v,w);add(v,u,w);    }    dfs(1,1);init_Xor();    for(int i=62 ;i>=0 ;i--) if((dis[n]^b[i]) > dis[n]) dis[n]^=b[i];    printf("%lld\n",dis[n]);    return 0;}