[BZOJ 2115]Xor

Description
在这里插入图片描述
Input
第一行包含两个整数 N N N M M M, 表示该无向图中点的数目与边的数目。 接下来 M M M 行描述 M M M 条边,每行三个整数 S i S_i Si T i T_i Ti D i D_i Di,表示 S i S_i Si T i T_i Ti之间存在 一条权值为 D i D_i Di的无向边。 图中可能有重边或自环。

Output
仅包含一个整数,表示最大的XOR和(十进制结果),注意输出后加换行回车。

Sample Input

5 7 

1 2 2 

1 3 2 

2 4 1 

2 5 1 

4 5 3 

5 3 4 

4 3 2 

Sample Output

6

思路
考虑这个图上存在的环,如果从环的起点走一圈回到环的起点后再走回点 1 1 1的话相当于只取到了这个环上所有边的异或值(从点 1 1 1到环的起点的边因为都走了两次,异或值抵消了)。
故只需要把所有环的异或值算出来丢到线性基里,再找一条从点 1 1 1到点 n n n的路径算出异或值,再以该值为基础去线性基里找可行的异或最大值即可。
关于可能有多条从点$1$到点$n$的路径的情况,这时它们必然也会被判断为是环(从点$1$到点$n$再从点$n$到点$1$)而将异或值丢进线性基里。这个异或值异或其中任意一条路径都可以得到另一条路径的异或值,所以最开始时可以随便找一条从点$1$到点$n$的路径算出异或值。

AC代码

#include<stdio.h>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<stdlib.h>
#include<string.h>
#define N 50005
using namespace std;
typedef long long ll;
ll n,m,t,s,va,cnt;
ll p[N*4],nxt[N*4],head[N],val[N*4],x[N*20],dis[N],vis[N];
typedef struct L_B
{
	private:
	ll b[70],p[70],flag,cnt;
	public:
	void insert(ll now)
	{
		for(int i=62;i>=0;--i)
		{
			if(now&(1ll<<i))
			if(this->b[i])now^=this->b[i];
			else 
			{
				this->b[i]=now;return;
			}
		}
		this->flag=1;
		return;
	}
	ll kth(ll k)//取第k大 
	{
		if(this->flag)--k;
		if(!k)return 0;
		ll ret=0;
		if(k>=(1ll<<this->cnt))return -1;
		for(int i=0;i<=this->cnt-1;++i)
			if(k&(1ll<<i))
			ret^=p[i];
		return ret;
	}
	void clear()
	{
		this->cnt=0;this->flag=0;
		for(ll i=0;i<64;i++)this->b[i]=0,this->p[i]=0;
	}
	void rebuild()//重构 
	{
		for(int i=1;i<=62;i++)
		if(this->b[i])
		{
			for(int j=0;j<i;j++)
			{
				if(this->b[i]&(1ll<<j))
				this->b[i]^=this->b[j];
			}
		}
		for(int i=0;i<=62;i++)
		{	
			if(this->b[i])p[(this->cnt)++]=this->b[i];
		}
		return;
	}
	ll get_max(ll now)//取最大值 
	{
		ll ret=now;	
		for(int i=62;i+1;--i)
		{
			if((ret^this->b[i])>ret)ret^=this->b[i];
		}
		return ret;
	}
	ll get_min()//取最小值 
	{
		if(this->flag)
			return 0;
		for(int i=0;i<=62;i++)
		{
			if(this->b[i])return this->b[i];
		}
		return 0;
	}
} L_B;
L_B B;
void dfs(ll now,ll va)
{
	vis[now]=1;dis[now]=va;
	for(ll i=head[now];i;i=nxt[i])
	{
		if(!vis[p[i]])dfs(p[i],va^val[i]);
		else x[++cnt]=va^val[i]^dis[p[i]];
	}
	return;
}
int main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld%lld",&s,&t,&va);
		p[i*2-1]=t;nxt[i*2-1]=head[s];head[s]=i*2-1;val[i*2-1]=va;
		p[i*2]=s;nxt[i*2]=head[t];head[t]=i*2;val[i*2]=va;
	}
	dfs(1,0);
	for(int i=1;i<=cnt;i++)B.insert(x[i]);
	B.rebuild();
	printf("%lld\n",B.get_max(dis[n]));
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值