线段树分治

引子

摘抄自二分图 /【模板】线段树分治题解

对于这类的问题

  • 有一些操作,每个操作只在 (l sim r) 时间段内有效。
  • 有一些询问,每个询问某一个时间点所有操作的贡献。

对于这样的询问,可以对时间建一棵线段树,对于每个操作,相当于在线段树上做区间操作

遍历整颗线段树,到达每个节点时执行相应的操作,然后继续向下递归,到达叶子节点时统计贡献,回溯时撤销操作即可。

这样的思想被称为线段树分治,可以在低时间复杂度内解决一类在线算法不优秀的问题。

二分图 /【模板】线段树分治

如何判断是否为二分图:

二分图:把图中所有点分成两个集合,使得所有边的两个顶点不在一个集合中。也就是一个不存在奇环的图

常用的方法是染色:如果是二分图,那么一条边的两个顶点一定是不同集合的,也就是颜色不同

还有一种方法就是值域并查集

  • 扩展域并查集:对于一个节点 (i) ,我们将其拆分为两个节点。一个属于集合 (S) ,另一个属于集合 (T) 。那么一条边所连接的两个节点就必须在不同的集合中。一个点在 (S) 中和在 (T) 的两个点属于一个集合,那么这张图就不是二分图。

对于时间端:

我们按照上面,对时间段建立线段树,每个节点开一个 vector,存下这个时间端内存在的边,遍历时,从根节点出发,每到一个节点,将挂在该节点上的所有边合并,然后递归处理左儿子和右儿子。如果发现有某条边合并会出现奇环,那么当前线段树节点所对应的时间区间都不会形成二分图。

当到达叶子节点时,如果合并了所有挂在当前节点上的边,依旧满足二分图的性质,那这个时刻就可以形成二分图。

回溯的时候,因为并查集不能删边,用可撤销并查集(按秩合并),用栈维护并查集的操作就好了。

按秩合并??:把高度小的树合并到高度大的树上,如果高度一样,合并后高度就 +1

保证了树的高度最多为 log n

按秩合并复杂度:(O(log~n)),线段树:(O(m~log~k))

总的复杂度:(O(logn imes mlogn))

code

/*
work by:Ariel_
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <stack>
#include <vector>
#include <utility>
#define ll long long
#define rg register
#define lson rt << 1
#define rson rt << 1|1
using namespace std;
const int N = 1e5 + 5;
const int M = 2e5 + 5;
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*10 + c - '0'; c = getchar();}
    return x*f;
}
struct node{int x, fag;};
int n, m, fa[N << 1], a[N << 1], k, h[N << 1], u[M], v[M];
struct Tree{int l, r; vector<int> e;}tree[N << 3];
stack<node> s;
int get_father(int x) { 
   while(x ^ fa[x]) x = fa[x];
   return x;
}
void merge(int x, int y) {
   if (x == y) return ;
   if (h[x] > h[y]) swap(x, y);
   s.push((node){x, h[x] == h[y]}), fa[x] = y, h[y] += h[x] == h[y]; //如果两棵树高度相同,那么合并之后高度会 + 1 
}
void build(int rt, int l, int r) {
   tree[rt].l = l, tree[rt].r = r;
   if (l == r) return;
   int mid = (l + r) >> 1;
   build(lson, l, mid), build(rson, mid + 1, r);
}
void ins(int rt, int L, int R, int id) {
    if (L <= tree[rt].l && tree[rt].r <= R) {
      tree[rt].e.push_back(id);
      return;
	}
	int mid = (tree[rt].l + tree[rt].r) >> 1;
	if (L <= mid) ins(lson, L, R, id);
	if (R > mid) ins(rson, L, R, id);
}
void DFS(int rt, int l, int r) {
    bool op = 1;//判断合不合法
	int mid = (l + r) >> 1, siz = (int)s.size();
	for (int i = 0; i < (int)tree[rt].e.size(); i++) {
	    int x = tree[rt].e[i], u = get_father(::u[x]), v = get_father(::v[x]);
	    if (u == v) { 
	       for (int i = l; i <= r; i++) printf("No
");
		   op = 0; break; 
		}
		merge(u, get_father(::v[x] + N)), merge(v, get_father(::u[x] + N));
	} 
	if (op) {
	   if (l == r) printf("Yes
");
	   else DFS(lson, l, mid), DFS(rson, mid + 1, r);
	}
	while (s.size() > siz) {
	    node t = s.top();
	    int x = t.x, fag = t.fag;
		h[fa[x]] -= fag, fa[x] = x, s.pop();
	}
}
int main(){
   n = read(), m = read(), k = read();
   for (int i = 1; i <= n; i++) fa[i] = i, fa[i + N] = i + N;
   build(1, 1, k);
   for (int i = 1, l, r; i <= m; i++) {
   	 u[i] = read(), v[i] = read(), l = read(), r = read();
   	 if(l ^ r) ins(1, l + 1, r, i);   
   }  
   DFS(1, 1, k);
   return 0;
}

原文地址:https://www.cnblogs.com/Arielzz/p/15054803.html