[WC2011]最大XOR和路径

link

和图论结合起来的一道题目,更多的利用的是异或这个运算本身的性质,线性基只是一个工具。

首先要理清楚一个问题我们最后形成的路径会是什么样子的?必然,我们有一种走法是直奔目标,但这很有可能并不是最优解。它可能半道误入歧途,绕个圈回来后再奔向终点。可以发现,主路径到环的这条路不会被计入答案,毕竟一个数异或自己等于0。那么,最后的答案便只可能是一条主路径加上一堆环。那么怎么搞?

首先你会发现这个主路径可能会有很多,枚举起来非常麻烦。对于这个问题有个很巧妙的解法,既然存在两条路径A和B都是从原点到终点,那么很显然,它们也会构成一个或多个环。假如我们把A作为主路径,那么把它的答案异或上大环的答案就会是B路径的答案。

其次是要如何枚举出所有的环?其实不必找出所有的环,可以考虑进行DFS,钢出来一棵DFS树之后找出那些只有一条非树边的环即可。剩下的环怎么办?拥有两条及以上非树边的环必然可以由多个上述的特殊环异或得到,因为这些特殊环各自的非树边可以构成我们要的环的非树边,而树边也可以得到,重复的树边也会被异或消除。大概原理和线性基相似,可以感性理解(说白了就是我不会证)。

最后一个问题,这样的特殊环可能有多少?由DFS的写法可知,这样的特殊环只有可能在一个已知点发现一条指向另一个已知点的时候才会被统计,那么每条边只可能会有一个贡献机会,所以环的个数和边数是同阶的,复杂度也就有了保证。

代码写出来很简洁。

#include<cstdio>
//#define zczc
#define ll long long
const int N=50010;
const int M=100010;
const int S=64;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}

int m,n;
struct edge{int t,next;ll v;}e[M<<1];
int esum,head[N];
inline void add(int fr,int to,ll val){e[++esum]=(edge){to,head[fr],val};head[fr]=esum;}

ll s[S];
void adds(ll wh){
	for(int i=S-1;i>=0&&wh;i--){
		if((wh&(1ll<<i))==0)continue;
		if(!s[i])s[i]=wh;wh^=s[i];
	}
}

ll d[N],ans;bool vis[N];
void dfs(int wh,ll dis){
	if(wh==m)ans=dis;vis[wh]=true;d[wh]=dis;
	for(int i=head[wh],th;i;i=e[i].next){
		if(vis[th=e[i].t])adds(dis^e[i].v^d[th]);
		else dfs(th,dis^e[i].v);
	}
}

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	int s1,s2;ll s3;
	read(m);read(n);
	for(int i=1;i<=n;i++){read(s1);read(s2);scanf("%lld",&s3);add(s1,s2,s3);add(s2,s1,s3);}
	dfs(1,0);
	for(int i=S-1;i>=0;i--)if((ans^s[i])>ans)ans^=s[i];
	printf("%lld\n",ans);
	
	return 0;
}
posted @ 2022-02-20 15:59  Feyn618  阅读(35)  评论(0编辑  收藏  举报