1. 程式人生 > >bzoj4025 二分圖(線段樹分治+帶權並查集維護路徑長奇偶性)

bzoj4025 二分圖(線段樹分治+帶權並查集維護路徑長奇偶性)

bzoj4025 二分圖

題意:
神犇有一個n個節點的圖。因為神犇是神犇,所以在T時間內一些邊會出現後消失。神犇要求出每一時間段內這個圖是否是二分圖。

資料範圍
n<=100000,m<=200000,T<=100000,1<=u,v<=n,0<=start<=end<=T。

題解:
判斷是否是二分圖即判斷是否沒有奇環。

考慮按st順序一條一條地加邊,如果加入一條邊,形成偶環,那麼對後面的邊沒有影響,
如果形成奇環,就不是二分圖,但是維護一個圖顯然不好維護,
那麼好維護的方式就是轉化成樹,
形成環時,如果刪掉這個環上的某一條邊,就還是樹,
為了好維護,選擇彈掉ed最小的那條邊。那麼如果成奇環,就是在這個ed最小的邊未消失之前都不是二分圖。
如果後面的某條邊本來會與此條目前被彈掉的邊成奇環,而刪掉這條邊則不能成環或是成偶環,
那麼前者不可能發生,因為只有原來那條邊會成環才不會加,
後者則原來那條邊成奇環,在統計貢獻時已經覆蓋了此條邊在其中會成奇環的時刻。
於是就是維護按ed的最大生成樹,用LCT。

但是其實可以按照線段樹分治的思想,每條邊存在的區間打上標記,在葉子節點輸出答案。
思路與上邊相似,只是這次成奇環時沒有彈邊,直接就這個區間的答案都是No即可。

關於這個並查集維護路徑奇偶性的正確性證明見這篇相當詳細的題解。

程式碼:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=100005;
const int M=200005;
int
n,m,T,S[2*M],top=0,fa[N],rank[N],c[N]; struct node { int u,v,st,ed; node(){} node(int u,int v,int st,int ed):u(u),v(v),st(st),ed(ed){} }; int getfa(int x) { if(fa[x]==x) return x; else return getfa(fa[x]); } int dis(int x) { if(fa[x]==x||!fa[x]) return 0; return c[x]^dis(fa[x]); } void
merge(int x,int y,int w) { if(rank[x]>rank[y]) swap(x,y); if(rank[x]==rank[y]) {S[++top]=-y; rank[y]++;} S[++top]=x; fa[x]=y; c[x]=w; } void res(int x) { while(top>x) { if(S[top]<0) rank[-S[top]]--; else {fa[S[top]]=S[top]; c[S[top]]=0;} top--; } } void query(int lf,int rg,vector<node> &E) { int pre=top; int mid=(lf+rg)>>1; vector<node> L,R; int sz=E.size(); for(int i=0;i<sz;i++) { if(E[i].st==lf&&E[i].ed==rg) { int x=E[i].u; int y=E[i].v; int fx=getfa(x); int fy=getfa(y); if(fx!=fy) { int w=dis(x)^dis(y)^1; merge(fx,fy,w); } else { int w=dis(x)^dis(y); if((w&1)==0) {for(int j=lf;j<=rg;j++) printf("No\n"); res(pre); return;} } } else { if(E[i].ed<=mid) L.push_back(E[i]); else if(E[i].st>mid) R.push_back(E[i]); else {L.push_back(node(E[i].u,E[i].v,E[i].st,mid)); R.push_back(node(E[i].u,E[i].v,mid+1,E[i].ed));} } } if(lf==rg){printf("Yes\n"); res(pre); return;} query(lf,mid,L); query(mid+1,rg,R); res(pre); } int main() { scanf("%d%d%d",&n,&m,&T); for(int i=1;i<=n;i++) fa[i]=i,rank[i]=0; vector<node> e; for(int i=1;i<=m;i++) { int u,v,st,ed; scanf("%d%d%d%d",&u,&v,&st,&ed); st++; if(st<=ed) e.push_back(node(u,v,st,ed)); } query(1,T,e); return 0; }