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

BZOJ4025 二分圖(線段樹分治+並查集)

data cto 距離 har oid bsp find vector 節點

之前學了一下線段樹分治,這還是第一次寫。思想其實挺好理解,即離線後把一個操作影響到的時間段拆成線段樹上的區間,並標記永久化。之後一塊處理,對於某個節點表示的時間段,影響到他的就是該節點一直到線段樹根的所有操作。(語死早)這樣可以把操作的插入和刪除改為只有插入。

具體到這題,由於並查集沒法刪除邊,我們考慮線段樹分治。之後要考慮的問題就是如何用並查集判斷是否為二分圖,也即是否含奇環。假設現在圖中有一個偶環,若給偶環兩點加了一條邊,可以發現無論去掉原偶環上哪一條邊都不會改變新出現環的奇偶性。於是我們只要用並查集維護出每個點到根的距離(按秩合並),從而維護出任意兩點距離的奇偶性,若加入一條環邊則判斷是否會構成奇環,有奇環直接退出,沒有則扔掉這條邊不管。每次處理完線段樹一個節點後要把操作還原,以避免影響其他無關節點。這個可以在操作時用棧記錄,退出時還原。

這個題就做完了。然而代碼能力極差的我發現自己連並查集都差點不會寫了。以及,圖中會有自環,有自環就不是二分圖,我開始竟然反方向判掉了……還有會有出現時刻與消失時刻相同的邊,RE了好幾發。總之調到吐血,早日退役保平安。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
using
namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<0||c>9) {if (c==-) f=-1;c=getchar();} while (c>=0&&c<=9) x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 100010 int n,m,T,L[N<<2],R[N<<2],fa[N],deep[N],dis[N],v[N];
struct edge{int x,y;}; struct data{int x,fa,deep,v;}; vector<edge> tree[N<<2]; stack<data> undo[N<<2]; bool ans[N]; void build(int k,int l,int r) { L[k]=l,R[k]=r; if (l==r) return; int mid=l+r>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); } void add(int k,int l,int r,edge e) { if (L[k]==l&&R[k]==r) {tree[k].push_back(e);return;} int mid=L[k]+R[k]>>1; if (r<=mid) add(k<<1,l,r,e); else if (l>mid) add(k<<1|1,l,r,e); else add(k<<1,l,mid,e),add(k<<1|1,mid+1,r,e); } int find(int x) { if (fa[x]==x) return x; int p=find(fa[x]); dis[x]=dis[fa[x]]^v[x]; return p; } void merge(int k,int x,int y,int p) { if (deep[x]<deep[y]) swap(x,y); data a; a.x=y;a.fa=x;a.deep=deep[x];a.v=v[y]; undo[k].push(a); fa[y]=x;v[y]=p;dis[y]=dis[x]^p; if (deep[y]==deep[x]) deep[x]++; } void solve(int k) { int s=tree[k].size(); bool flag=0; for (int i=0;i<s;i++) { if (tree[k][i].x==tree[k][i].y) {flag=1;break;} int p=find(tree[k][i].x),q=find(tree[k][i].y); if (p!=q) merge(k,p,q,dis[tree[k][i].x]^dis[tree[k][i].y]^1); else if (dis[tree[k][i].x]^dis[tree[k][i].y]^1) {flag=1;break;} } if (L[k]<R[k]&&!flag) solve(k<<1),solve(k<<1|1); else if (flag) for (int i=L[k];i<=R[k];i++) ans[i]=1; while (!undo[k].empty()) { data a=undo[k].top(); fa[a.x]=a.x; deep[a.fa]=a.deep; dis[a.x]=v[a.x]=a.v; undo[k].pop(); } } int main() { freopen("bzoj4025.in","r",stdin); freopen("bzoj4025.out","w",stdout); n=read(),m=read(),T=read(); build(1,1,T); for (int i=1;i<=m;i++) { edge e;e.x=read(),e.y=read(); int s=read()+1,t=read(); if (s<=t) add(1,s,t,e); } for (int i=1;i<=n;i++) fa[i]=i,deep[i]=v[i]=dis[i]=1; solve(1); for (int i=1;i<=T;i++) if (ans[i]) printf("No\n"); else printf("Yes\n"); fclose(stdin);fclose(stdout); return 0; }

BZOJ4025 二分圖(線段樹分治+並查集)