[CDQ分治][带权并查集]JZOJ 4769
分析
我们发现其实只是图上的边会在[l,r]的时间内存在罢了
那么我们可以来一波CDQ
用一个vector存边,然后如果跨越当前区间的边就拆到左边或右边的边集L,R中
同时要存一下并查集的状态,不能路径压缩否则无法还原,用按秩合并和栈记录并查集存边顺序,跳出当前层分治时还原并查集
#pragma GCC optimize(2) #include <iostream> #include <cstdio> #include <vector> using namespace std; const int N=3e5+10; struct Edge { int u,v,l,r; }; vector<Edge> g; int f[N],r[N],stk[N],d[N]; int n,m,top; bool ans[N]; int Find(int x) {return f[x]==x?x:Find(f[x]);} int dist(int x) {return f[x]==x?0:d[x]^dist(f[x]);} void Merge(int i,int j,int dist) { if (r[i]>r[j]) swap(i,j); if (r[i]==r[j]) r[j]++,stk[++top]=-j; f[i]=j;d[i]=dist;stk[++top]=i; } void Pop(int now) { for (;top>now;top--) { if (stk[top]<0) r[-stk[top]]--; else f[stk[top]]=stk[top],d[stk[top]]=0; } } void Divide(int l,int r,vector<Edge> g) { int mid=l+r>>1,now=top; vector<Edge> ll,rr; for (vector<Edge>::iterator i=g.begin();i!=g.end();i++) if (i->l==l&&i->r==r) { int u=Find(i->u),v=Find(i->v),d=dist(i->u)^dist(i->v)^1; if (u!=v) Merge(u,v,d); else if (d&1) { for (int j=l;j<=r;j++) ans[j]=1; Pop(now); return; } } else if (i->r<=mid) ll.push_back(*i); else if (i->l>mid) rr.push_back(*i); else { ll.push_back((Edge){i->u,i->v,i->l,mid}); rr.push_back((Edge){i->u,i->v,mid+1,i->r}); } if (l!=r) Divide(l,mid,ll),Divide(mid+1,r,rr); Pop(now); } int main() { freopen("graph.in","r",stdin); freopen("graph.out","w",stdout); scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { int order; scanf("%d",&order); if (order) { int u,v; scanf("%d%d",&u,&v); g.push_back((Edge){u,v,i,m}); } else { int j; scanf("%d",&j); g[j].r=i-1; } } for (int i=0;i<n;i++) f[i]=i; Divide(1,m,g); for (int i=1;i<=m;i++) printf(ans[i]?"NO\n":"YES\n"); }
在日渐沉没的世界里,我发现了你。