不知道为什么要把这两个没什么关系的算法放到一起写...可能是都很黑科技?
1.线段树分治
例题:bzoj4026 二分图
给你一个图,资瓷加一条边,删一条边,询问当前图是不是二分图
如果用 LCT 的话我们要维护关于删除时间的最大生成树,然后每进来一条边判断奇环,就很难写
线段树分治可以很好的解决这种有插入有删除单点询问的问题
如果没有加入边和删除边的操作,显然我们可以用带权并查集判断奇环解决
然后我们就要考虑怎么把带加入和带删除的问题转换成没有加入和删除的问题
我们记一下每条边存在的时间区间,把这些时间区间放到线段树上
对于线段树的每一个区间,我们加入时间区间正好完全包含这个区间的边,判断有没有奇环
有奇环,这一段全都是 No ,没有奇环,我们递归到左右区间分别判断
如果递归到了 $l == r$ 也就是一个时间的单点,还没有奇环的话,这个单点就是 Yes
中间判断奇环的部分,一个节点所用到的边是它和它所有祖先的边,所以处理完一个区间要把多余的边删掉
这个可以用按秩合并的可撤销并查集,这个并查集还要维护每个点到根节点距离的奇偶性
这样做的好处是,把带插入带删除的问题,转换成了没有插入没有删除的 sb 题
复杂度的话,就是一个线段树区间修改 + 整体询问的复杂度
所以是 $O(nlogn)$ 的
#include<bits/stdc++.h> #define LL long long #define SET vector<edge> using namespace std; const int maxn = 2e5 + 10; inline int read() { int x = 0,f = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f; for(;isdigit(ch);x = 10 * x + ch - '0',ch = getchar()); return x * f; } int n,m,T; struct edge { int u,v,l,r; bool operator < (const edge &b)const { if(l == b.l) return r < b.r; return l < b.l; } }; SET S; int top = 0; namespace ufsufs { struct node{int fa,val,size;}t[maxn]; struct info{int x,y;node a,b;}st[maxn]; inline void init(){for(int i=1;i<=n;i++) t[i] = (node){i, 0, 1};} inline int find(int x){while(t[x].fa != x) x = t[x].fa;return x;} inline int dis(int x) { int ret = 0; for(;t[x].fa != x;x = t[x].fa)ret ^= t[x].val; return ret; } inline void link(int x,int y) { int val = dis(x) ^ dis(y) ^ 1; x = find(x),y = find(y); st[++top] = (info){x,y,t[x],t[y]}; if(t[x].size > t[y].size)swap(x,y); t[x].fa = y;t[x].val = val;t[y].size += t[x].size; } inline void rec(int bot) { while(top != bot) { info &now = st[top--]; t[now.x] = now.a;t[now.y] = now.b; } } } using namespace ufsufs; inline void SegDiv(int l,int r,SET &S) { int mid = (l + r) >> 1,bot = top; SET ls,rs; for(int i=0;i<(int)S.size();i++) { edge &now = S[i]; int x = now.u,y = now.v; if(now.l == l && now.r == r) { int fx = find(x),fy = find(y); if(fx == fy) { int val = dis(x) ^ dis(y); if(!val) { for(int i=l;i<=r;i++)puts("No"); rec(bot);return; } } else link(x,y); } else if(now.r <= mid)ls.push_back(now); else if(now.l > mid)rs.push_back(now); else ls.push_back( (edge){now.u, now.v, now.l, mid} ), rs.push_back( (edge){now.u, now.v, mid+1, now.r} ); } if(l == r)puts("Yes"); else SegDiv(l,mid,ls),SegDiv(mid + 1,r,rs); rec(bot); } int main() { n = read(),m = read(),T = read(); for(int i=1;i<=m;i++) { int u = read(),v = read(),l = read() + 1,r = read(); if(l > r)continue; S.push_back((edge){u,v,l,r}); } init(); SegDiv(1,T,S); }