1. 程式人生 > >Codeforces 938G Shortest Path Queries 線段樹分治+並查集+線性基

Codeforces 938G Shortest Path Queries 線段樹分治+並查集+線性基

題意

給出一個連通帶權無向圖,邊有邊權,要求資瓷q個操作:
1 x y d在原圖中加入一條x到y權值為b的邊
2 x y把圖中x到y的邊刪掉
3 x y表示詢問x到y的異或最短路
保證任意操作後原圖連通無重邊自環且操作均合法
n,m,q<=200000

分析

如果沒有前兩個操作的話,我們就可以把原圖的dfs樹建出來,那麼x到y的異或最短路就是它們在dfs樹上的路徑再異或上任意個由返祖邊組成的環。這個顯然可以用線性基來維護。
現在有了加邊和刪邊,那麼我們可以用線段樹分治來做,把每條邊放到O(log)個區間上,然後分治。
設f[x]表示dfs樹上x到根的路徑異或和,可以通過按秩合併並查集來維護f[x],然後每次退棧的時候撤銷即可。
每個分治節點維護一個線性基,每次把線性基複製一遍傳給下一層即可。
一開始wa了是因為在並查集合並的時候,並查集上新增那條邊的權值應該是f[x] xor f[y] xor 圖中新增邊的權值,而我少加了一個f[x]。關鍵是我少加了居然還能過對拍。

程式碼

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
using namespace std;

const int N=200005;

typedef pair<int,int> pi;

int
n,m,val[N],f[N],size[N],stack[N],top,bas[35][35],bin[35],cnt,tot; pi qu[N]; struct edge{int u,v,w,s,t;}e[N*2]; map<pi,int> ma; vector<int> vec[N*4]; int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while (ch>='0'&&ch<='9'
){x=x*10+ch-'0';ch=getchar();} return x*f; } int find(int x) { while (f[x]!=x) x=f[x]; return x; } int get_val(int x) { int w=0; while (f[x]!=x) w^=val[x],x=f[x]; return w; } void add(int d,int dep) { for (int i=0;i<vec[d].size();i++) { int id=vec[d][i],u=e[id].u,v=e[id].v,x=find(u),y=find(v); if (x!=y) { if (size[x]>size[y]) swap(u,v),swap(x,y); f[x]=y;size[y]+=size[x];val[x]=get_val(u)^get_val(v)^e[id].w;stack[++top]=x; } else { int w=get_val(u)^get_val(v)^e[id].w; for (int i=30;i>=0;i--) if (w&bin[i]) { if (!bas[dep][i]) {bas[dep][i]=w;break;} else w^=bas[dep][i]; } } } } void del(int tmp) { while (top>tmp) { int x=stack[top];top--; size[f[x]]-=size[x];f[x]=x;val[x]=0; } } void ins(int d,int l,int r,int x,int y,int id) { if (x>y) return; if (l==x&&r==y) {vec[d].pb(id);return;} int mid=(l+r)/2; if (y<=mid) ins(d*2,l,mid,x,y,id); else if (x>mid) ins(d*2+1,mid+1,r,x,y,id); else ins(d*2,l,mid,x,mid,id),ins(d*2+1,mid+1,r,mid+1,y,id); } void solve(int d,int l,int r,int dep) { int tmp=top;add(d,dep); if (l==r) { int x=qu[l].first,y=qu[l].second,w=get_val(x)^get_val(y); for (int i=30;i>=0;i--) if ((w^bas[dep][i])<w) w^=bas[dep][i]; printf("%d\n",w);del(tmp); return; } int mid=(l+r)/2; for (int i=0;i<=30;i++) bas[dep+1][i]=bas[dep][i]; solve(d*2,l,mid,dep+1); for (int i=0;i<=30;i++) bas[dep+1][i]=bas[dep][i]; solve(d*2+1,mid+1,r,dep+1); del(tmp); } int main() { bin[0]=1; for (int i=1;i<=30;i++) bin[i]=bin[i-1]*2; n=read();m=read(); for (int i=1;i<=m;i++) { int x=read(),y=read(),z=read(); e[++cnt].u=x;e[cnt].v=y;e[cnt].w=z; ma[mp(x,y)]=cnt;e[cnt].s=1;e[cnt].t=-1; } int q=read(); for (int i=1;i<=q;i++) { int op=read(),x=read(),y=read(); if (op==1) { int d=read(); e[++cnt].u=x;e[cnt].v=y;e[cnt].w=d; ma[mp(x,y)]=cnt;e[cnt].s=tot+1;e[cnt].t=-1; } else if (op==2) e[ma[mp(x,y)]].t=tot,ma[mp(x,y)]=0; else qu[++tot]=mp(x,y); } for (int i=1;i<=cnt;i++) ins(1,1,tot,e[i].s,e[i].t==-1?tot:e[i].t,i); for (int i=1;i<=n;i++) f[i]=i,size[i]=1; solve(1,1,tot,0); return 0; }