1. 程式人生 > 實用技巧 >P2420 讓我們異或吧(洛谷)

P2420 讓我們異或吧(洛谷)

題目描述

異或是一種神奇的運算,大部分人把它總結成不進位加法.

在生活中…xor運算也很常見。比如,對於一個問題的回答,是為1,否為0.那麼:

(A是否是男生 )xor( B是否是男生)=A和B是否能夠成為情侶

好了,現在我們來製造和處理一些複雜的情況。比如我們將給出一顆樹,它很高興自己有N個結點。樹的每條邊上有一個權值。我們要進行M次詢問,對於每次詢問,我們想知道某兩點之間的路徑上所有邊權的異或值。

輸入格式

輸入檔案第一行包含一個整數N,表示這顆開心的樹擁有的結點數,以下有N-1行,描述這些邊,每行有3個數,u,v,w,表示u和v之間有一條權值為w的邊。接下來一行有一個整數M,表示詢問數。之後的M行,每行兩個數u,v,表示詢問這兩個點之間的路徑上的權值異或值。

輸出格式

輸出M行,每行一個整數,表示異或值

輸入輸出樣例

輸入 #1
5
1 4 9644
2 5 15004
3 1 14635
5 3 9684
3
2 4
5 4
1 1

輸出 #1
975
14675
0

說明/提示

對於40%的資料,有1 ≤ N,M ≤ 3000;

對於100%的資料,有1 ≤ N ,M≤ 100000。

AC程式碼:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
using
namespace std; #define re register //由於本題目給出的資料量較大,因此存樹時不用鄰接矩陣而是鏈式前向星 //此題出現TLE的原因是在for迴圈中加入了dfs,這個不是一個合理的程式設計方式,因此非常容易出現TLE的情況 //改進方式:我們需要對此題有更深層次的認識,核心點:兩次異或等於自身!!。因此我們記錄下每個點到dfs的起始點的所有異或值 //求任意兩點之間的所有路徑異或值即讓兩個點到起始點的異或值進行異或即可(因為相同的路徑部分會自動消去所以不需要過多關注) int cnt=0; //邊的編號是從0開始的 int head[100005]; short color[100005
]; //用於標記結點是否被訪問過 int n,m; int flag=0; //標誌 int dis[100005]={0}; //記錄每個點到dfs搜尋起始點的所有異或值 typedef struct Edge{ //關於邊的結構體 int w; //權值 int to; //終點 int next; //下一條邊 }E; E e[200005]; //由於是無向圖 所以需要考慮兩倍的邊量 void add(int u,int v,int w){ //套路加邊模板,對於無向圖,每條邊需要add兩次 e[cnt].w=w; e[cnt].to=v; e[cnt].next=head[u]; head[u]=cnt++; } void dfs(int a,int b,int sum){ //sum代表當前總的異或值,b是目標點,a在此處看成當前出發點 if(flag) return; if(a==b){ //表示a已經到達b點 直接輸出結果返回 // cout<<sum<<endl; flag=1; return; } color[a]=1; dis[a]^=sum; for(re int k=head[a];~k;k=e[k].next){ //對以a為起點(和a連線的)的所有的邊進行遍歷 if(color[e[k].to]==0){ dfs(e[k].to,b,sum^e[k].w); } } color[a]=0; //此處回溯不回溯無所謂 因為是樹狀 } int main(){ cin>>n; int u,v,w; memset(head,-1,sizeof(head)); for(re int i=1;i<=n-1;i++){ // cin>>u>>v>>w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); add(v,u,w); //無向 } flag=0; memset(color,0,sizeof(color)); dfs(1,0,0); //假設1為起始搜尋點,0位搜尋終點,由於0點不存在,因此會遍歷整個圖 cin>>m; int a,b; for(re int i=1;i<=m;i++){ // cin>>a>>b; scanf("%d%d",&a,&b); printf("%d\n",dis[a]^dis[b]); } return 0; }