01Trie树【p2420】 让我们异或吧

Description

异或是一种神奇的运算,大部分人把它总结成不进位加法.

在生活中…xor运算也很常见。比如,对于一个问题的回答,是为1,否为0.那么:

(A是否是男生 )xor( B是否是男生)=A和B是否能够成为情侣

好了,现在我们来制造和处理一些复杂的情况。比如我们将给出一颗树,它很高兴自己有N个结点。树的每条边上有一个权值。我们要进行M次询问,对于每次询问,我们想知道某两点之间的路径上所有边权的异或值。

Input

输入文件第一行包含一个整数N,表示这颗开心的树拥有的结点数,以下有N-1行,描述这些边,每行有3个数,u,v,w,表示u和v之间有一条权值为w的边。接下来一行有一个整数M,表示询问数。之后的M行,每行两个数u,v,表示询问这两个点之间的路径上的权值异或值。

Output

输出M行,每行一个整数,表示异或值

 很明显,树上问题求解两节点间的问题,直接考虑到(LCA)

我们记录(dis[u])代表从(u)到根节点的路径异或值

那么我们可以在(dfs)预处理倍增数组的时候预处理出来我们的(dis)数组.

(dis)数组有什么用?

如图,我们询问节点(x->y)的路径异或值。

此时已知(dis[x])(dis[y]),根据(xor)的性质。

可得(x->y)的路径异或值为((dis[x] xor dis[lca_{x,y}]) xor (dis[y] xor dis[lca_{x,y}]))

直接求解即可

代码

#include<cstdio>
#include<cctype>
#include<algorithm> 
#define N 100008
#define R register
using namespace std;
inline void in(int &x)
{
	int f=1;x=0;char s=getchar();
	while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
	while(isdigit(s)){x=x*10+s-'0';s=getchar();}
	x*=f;
}
int depth[N],n,m,dis[N],f[N][21],head[N],tot;
struct cod{int u,v,w;}edge[N<<2];
inline void add(int x,int y,int z)
{
	edge[++tot].u=head[x];
	edge[tot].v=y;
	edge[tot].w=z;
	head[x]=tot;
}
void dfs(int u,int fa,int w)
{
	depth[u]=depth[fa]+1;dis[u]=dis[fa]^w;
	for(R int i=1;(1<<i)<=depth[u];i++)
		f[u][i]=f[f[u][i-1]][i-1];
	for(R int i=head[u];i;i=edge[i].u)
	{
		if(edge[i].v==fa)continue;
		dfs(edge[i].v,u,edge[i].w);
	}
}
inline int lca(int x,int y)
{
	if(depth[x]>depth[y])swap(x,y);
	for(R int i=17;i>=0;i--)
		if(depth[x]+(1<<i)<=depth[y])
			y=f[y][i];
	if(x==y)return x;
	for(R int i=17;i>=0;i--)
	{
		if(f[x][i]==f[y][i])continue;
		x=f[x][i],y=f[y][i];
	}
	return f[x][0];
}
int main()
{
	in(n);
	for(R int i=1,x,y,z;i<n;i++)
	{
		in(x),in(y),in(z);
		add(x,y,z);add(y,x,z);
	}
	dfs(1,0,0);
	in(m);
	for(R int x,y,la;m;m--)
	{
		in(x),in(y);
		if(x==y)
		{
			puts("0");
			continue;
		}
		la=lca(x,y);
		printf("%d
",(dis[x]^dis[la])^(dis[y]^dis[la]));
	}
}

不太理解为什么要建双向边,

如果有大佬懂的话能否告知蒟蒻我 qwq

原文地址:https://www.cnblogs.com/-guz/p/9763042.html