1. 程式人生 > >[BZOJ4025]二分圖(線段樹分治,並查集)

[BZOJ4025]二分圖(線段樹分治,並查集)

esc HR gpo 但是 limit esp ini += val

4025: 二分圖

Time Limit: 20 Sec Memory Limit: 512 MB
Submit: 2191 Solved: 800
[Submit][Status][Discuss]

Description

神犇有一個n個節點的圖。因為神犇是神犇,所以在T時間內一些邊會出現後消失。神犇要求出每一時間段內這個圖是否是二分圖。這麽簡單的問題神犇當然會做了,於是他想考考你。

Input

輸入數據的第一行是三個整數n,m,T。 第2行到第m+1行,每行4個整數u,v,start,end。第i+1行的四個整數表示第i條邊連接u,v兩個點,這條邊在start時刻出現,在第end時刻消失。

Output

輸出包含T行。在第i行中,如果第i時間段內這個圖是二分圖,那麽輸出“Yes”,否則輸出“No”,不含引號。

Sample Input

3 3 3
1 2 0 2
2 3 0 3
1 3 1 2

Sample Output

Yes
No
Yes

HINT

樣例說明:
0時刻,出現兩條邊1-2和2-3。
第1時間段內,這個圖是二分圖,輸出Yes。
1時刻,出現一條邊1-3。
第2時間段內,這個圖不是二分圖,輸出No。
2時刻,1-2和1-3兩條邊消失。
第3時間段內,只有一條邊2-3,這個圖是二分圖,輸出Yes。

數據範圍:
n<=100000,m<=200000,T<=100000,1<=u,v<=n,0<=start<=end<=T。

Source

[Submit][Status][Discuss]

挺妙的思想,線段樹不僅僅是一種數據結構,還是一種處理問題的思想,在圖論的建圖等方面也有用到。

這個方法全稱應該叫“線段樹對時間CDQ分治”,用於處理有時間參數的修改與詢問問題。但是有一個前提,就是一個時間區間的修改可以分成互不影響的兩個時間區間的修改,比如這題的邊(u,v,start,end)可以分裂為(u,v,start,mid)和(u,v,mid+1,end)。

這題聽說LCT可做,但還有一種思路,CDQ(L,R,A)表示詢問在[L,R]中,關於這個詢問(且還未被處理)的修改集合為A的分治過程。具體流程是:

1.遍歷A,對於所有完全包含[L,R]的修改操作進行處理。

2.將A中所有不完全包含[L,R]的修改操作按照線段樹的分法分到包含[L,mid]和[mid+1,R]的兩個區間中,用集合B和C記錄這些修改。

3.分別遞歸[L,mid]和[mid+1,R],如果L=R則可以直接特判回溯。

4.撤銷本層所有操作,保證後續回溯與遞歸正確進行。

回到這一題,所謂修改就是記錄距離的並查集操作(因為判斷二分圖的方法就是查找裏面是否有奇環),所以這裏的並查集必須支持撤銷,不能路徑壓縮。總復雜度$O(n\log^2 n)$

 1 #include<cstdio>
 2 #include<vector>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=l; i<=r; i++)
 5 typedef long long ll;
 6 using namespace std;
 7 
 8 const int N=200100;
 9 int n,m,T,u,v,l,r,top;
10 struct E{int u,v,l,r; bool operator<(const E &a)const{ return (l==a.l)?r<a.r:l<a.l; } };
11 vector<E> a;
12 struct P{ int fa,val,sz; }t[N];
13 struct D{ int x,y; P a,b; }stk[N];
14 
15 void init(){ rep(i,1,n) t[i]=(P){i,0,1}; }
16 int find(int x){ while (t[x].fa!=x) x=t[x].fa; return x;}
17 int dis(int x){
18     int res=0;
19     while (t[x].fa!=x) res^=t[x].val,x=t[x].fa;
20     return res;
21 }
22 
23 void link(int x,int y){
24     int val=dis(x)^dis(y)^1;
25     x=find(x); y=find(y);
26     stk[++top]=(D){x,y,t[x],t[y]};
27     if (t[x].sz>t[y].sz) swap(x,y);
28     t[x].fa=y; t[x].val=val; t[y].sz+=t[x].sz;
29 }
30 
31 void recov(int k){ while (top!=k) t[stk[top].x]=stk[top].a,t[stk[top].y]=stk[top].b,top--; }
32 
33 void CDQ(int L,int R,vector<E> &a){
34     int mid=(L+R)>>1,bot=top;
35     vector<E> b,c;
36     for (int i=0; i<(int)a.size(); i++){
37         E now=a[i]; int x=now.u,y=now.v;
38         if (now.l==L && now.r==R){
39             int p=find(x),q=find(y);
40             if (p==q){
41                 int val=dis(x)^dis(y);
42                 if (val==0){
43                     rep(i,L,R) puts("No");
44                     recov(bot); return;
45                 }
46             }else link(x,y);
47         }else if (now.r<=mid) b.push_back(now);
48             else if (now.l>mid) c.push_back(now);
49                 else b.push_back((E){now.u,now.v,now.l,mid}),c.push_back((E){now.u,now.v,mid+1,now.r});
50     }
51     if (L==R) puts("Yes"); else CDQ(L,mid,b),CDQ(mid+1,R,c);
52     recov(bot);
53 }
54 
55 int main(){
56     freopen("bzoj4025.in","r",stdin);
57     freopen("bzoj4025.out","w",stdout);
58     scanf("%d%d%d",&n,&m,&T);
59     rep(i,1,m){
60         scanf("%d%d%d%d",&u,&v,&l,&r); l++;
61         if (l<=r) a.push_back((E){u,v,l,r});
62     }
63     init(); CDQ(1,T,a);
64     return 0;
65 }

[BZOJ4025]二分圖(線段樹分治,並查集)