题目背景
(YSGH)牛逼
题目描述
给定(n)个点(m)条边的无向简单联通图(G),边有边权。保证没有重边和自环。
定义一条简单路径的权值为路径上所有边边权的异或和。
保证(G)中不存在简单环使得边权异或和不为(0)。
(Q)次询问(x)到(y)的最短简单路径。
输入格式
第一行三个正整数(n,m,Q)。
接下来(m)行,一行三个非负整数(x,y,v(1 leq x,y leq n)),表示一条连接(x,y),权值为(v)的无向边。保证没有重边和自环。
接下来(Q)行,一行两个正整数(x,y(1 leq x,y leq n)),表示一次询问。
输出格式
(Q)行,一行一个整数表示答案。
输入输出样例
输入 #1
3 2 1
1 2 2
2 3 3
1 3
输出 #1
1
说明/提示
数据点编号 | (n,Qleq) | 特殊性质 |
---|---|---|
(1,2) | (10) | 无 |
(3,4) | (20) | 无 |
(5,6) | (10^5) | (m = n-1) |
(7,8) | (10^5) | (v leq 1) |
(9,10) | (10^5) | 无 |
对于所有数据,满足(m leq 2 imes n,v < 2^{30})。
刚看到这个题实在没什么想法,因为对带有异或和类的题目做的太少。
后来听人讲才想起来,这个题的突破口在于任意一个环的异或和都是(0)。以及我们还需要一个推论,从一条路径增广到另一条路径的前提是这两条路径必须能够构成一个环,也就是这两条路径的异或和的异或和是(0)。所以,这两条路径异或和相等。
对上述结论推广,所以,任意两点之间的所有合法路径异或和相等。
也就是说,我们只需要找到两点间任意一条合法路径即可。
根据此优化图的模型,我们发现只要两点之间有路即可,即我们可以将其优化成一棵树。所以我们可以用(O(n))的时间预处理每个点到根路径的异或和,对于每组询问的(x,y),只需要输出(dp[x]veebar dp[y]),中间那个符号是逻辑里的异或。这是因为从(lca(x,y))到根的路径走了两次,抵消掉了,再次利用了异或的性质。
上代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<algorithm>
#define int long long
#define rep(i,a,n) for(register int i=a;i<=n;i++)
#define dep(i,n,a) for(register int i=n;i>=a;i--)
using namespace std;
int n,m,head[100050],dp[100050],num,num1,head1[100050],fa[100050],q;
struct edge
{
int u,v,c,nxt;
}e[1000500],e1[100050];
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
void write(int x)
{
if(x<0)putchar('-'),x=-x;
if(x==0)return;
write(x/10);
putchar(x%10+'0');
}
void add(int u,int v,int c)
{
e[++num].u=u,e[num].v=v,e[num].c=c;
e[num].nxt=head[u];head[u]=num;
}
void add1(int u,int v,int c)
{
e1[++num1].u=u,e1[num1].v=v,e1[num1].c=c;
e1[num1].nxt=head1[u];head1[u]=num1;
}
int f(int o)
{
if(fa[o]==o)return o;
return fa[o]=f(fa[o]);
}
void u(int a,int b)
{
int c=f(a),d=f(b);
fa[max(c,d)]=fa[min(c,d)];
}
void DP(int x,int fa)
{
for(register int st=head1[x];~st;st=e1[st].nxt)
{
int y=e1[st].v;
if(y==fa)continue;
dp[y]=dp[x]^e1[st].c;
DP(y,x);
}
return;
}
signed main()
{
memset(head,-1,sizeof head);
memset(head1,-1,sizeof head1);
n=read(),m=read(),q=read();
int a,b,c;
rep(i,1,m)
{
a=read(),b=read(),c=read();
add(a,b,c);
add(b,a,c);
}
int cnt=0;
rep(i,1,n)fa[i]=i;
rep(i,1,num)
{
int x=e[i].u,y=e[i].v;
if(f(x)!=f(y))
{
++cnt;
u(x,y);
add1(x,y,e[i].c);
add1(y,x,e[i].c);
}
if(cnt==n-1)break;
}//连成一棵生成树
DP(1,-1);
rep(i,1,q)
{
a=read(),b=read();
int ans=dp[a]^dp[b];
if(ans)write(ans);
else putchar('0');
putchar('
');
}
return 0;
}