題目背景
\(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,Q\leq\) | 特殊性質 |
---|---|---|
\(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\times 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('\n');
}
return 0;
}