蓝书(算法竞赛进阶指南)刷题记录——【WC2011】BZOJ2115 Xor(线性基)

题目:BZOJ2115.
题目大意:给定一张 n n n个点 m m m条边的无向图,求一条 1 1 1 n n n的路径使得边权异或和最大,输出最大边权异或和.
1 ≤ n ≤ 5 ∗ 1 0 4 , 1 ≤ m ≤ 1 0 5 1\leq n\leq 5*10^4,1\leq m\leq 10^5 1n5104,1m105,边权 ≤ 1 0 18 \leq 10^{18} 1018.

首先要想到一个性质,任何一条路径 ( 1 , n ) (1,n) (1,n)都可以通过另外一条路径 ( 1 , n ) (1,n) (1,n)通过异或上一些环的异或和来得到.

为什么呢?由于一条路径 ( 1 , n ) (1,n) (1,n)与另一条路径 ( 1 , n ) (1,n) (1,n)必然可以通过删除一些路径段再加上一些路径段来获得,而我们要求的是异或和,而异或的逆运算为异或,每一条删除的路径段也必然对应着一条要加入的路径段使得这两条路径段可以合并成一个环,所以这个性质是正确的.

有了这个性质后,我们就可以把所有路径都对应成一条 ( 1 , n ) (1,n) (1,n)的路径和一堆环;又考虑到是最大异或和,线性基的经典运用啊,然后我们就可以把所有环放到一起跑个线性基就好了.

代码如下:

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=50000,M=100000,C=63;

struct side{
  int y,next;
  LL v;
}e[M*2+9];
int lin[N+9],top;

void ins(int x,int y,LL v){
  e[++top].y=y;e[top].v=v;
  e[top].next=lin[x];
  lin[x]=top;
}

int n,m,cv,b[N+9];
LL v[M*2+9],d[N+9],ans;

void dfs(int k){
  b[k]=1;
  for (int i=lin[k];i;i=e[i].next)
    if (!b[e[i].y]){
      d[e[i].y]=d[k]^e[i].v;
      dfs(e[i].y);
	}else v[++cv]=d[k]^d[e[i].y]^e[i].v;
}

int Gauss(LL *a,int n){
  int now=1,r;
  for (int i=C;i>=0;--i){
  	for (r=now;r<=n;++r)
  	  if (a[r]>>i&1) break;
  	if (r>n) continue;
    if (now^r) swap(a[now],a[r]);
    for (int k=1;k<=n;++k)
      if (k^now&&a[k]>>i&1)
        a[k]^=a[now];
    ++now;
  }
  return now-1;      //没有返回值直接WA掉...
}

Abigail into(){
  scanf("%d%d",&n,&m);
  int x,y;LL v;
  for (int i=1;i<=m;++i){
  	scanf("%d%d%lld",&x,&y,&v);
  	ins(x,y,v);ins(y,x,v);
  }
}

Abigail work(){
  dfs(1);
  cv=Gauss(v,cv);
  ans=d[n];
  for (int i=1;i<=cv;++i)
    ans=max(ans,ans^v[i]);
}

Abigail outo(){
  printf("%lld\n",ans);
}

int main(){
  into();
  work();
  outo();
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值