线段树分治

线段树分治,不是一种数据结构,而是一种思想。

这里的分治,指的是对时间维度建立线段树

它通常用来解决带撤销问题,即:某一个元素会在某一段时间存在,然后询问任意一个时间点的一些信息。

具体地操作是:对于每一个元素,把它保存在时间线段树所对应的 \(O(logT)\) 个节点上,然后对线段树 \(dfs\),当进入离开一个线段树节点的时候,分别把节点信息在数据结构中加入或删除。

那么正常删除 \(1\) 次即可,现在要删除多次不是更麻烦了吗?并不是。现在的删除是删除最后的几次操作,也就是直接版本回退,这样一些原本不能直接删除的东西就可以做了。

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

题目

Description

有一个 \(n\) 个节点的图。

接下来的长为 \(k\) 的时间内有 \(m\) 条边存在一段时间。

求每一时间点这个图是否是二分图。

\(n,k = 10^5\)\(m = 2\times 10^5\)\(1 \le x,y \le n\)\(0 \le l \le r \le k\)

Solution

对每条边在时间上线段树分治。

拆点,用并查集维护二分图连通性,按秩合并不能路径压缩。

开一个全局的栈记录并查集的操作用来回退。

Code

#include <bits/stdc++.h>
#define N 200005
#define mid ((l+r)>>1)
using namespace std;
int n,m,k,tot,f[N],sz[N],s1[N],s2[N],st;
struct query{int u,v;}a[N];
vector<int>q[N<<1];
inline void addsum(int p,int l,int r,int L,int R,int k){
	if(L<=l&&r<=R)return q[p].push_back(k),void();
	if(L<=mid)addsum(p<<1,l,mid,L,R,k);
	if(R>mid)addsum(p<<1|1,mid+1,r,L,R,k);
}
inline int fa(int x){return f[x]==x?x:fa(f[x]);}
inline void merge(int x,int y){
	x=fa(x),y=fa(y);
	if(sz[x]>sz[y])swap(x,y);
	s1[++st]=x,s2[st]=y;
	sz[y]+=sz[x],f[x]=y;
}
inline void dfs(int p,int l,int r){
	bool sf=1;
	int tmp=st;
	for(int i:q[p]){
		if(fa(a[i].u)==fa(a[i].v)){sf=0;break;}
		merge(a[i].u,a[i].v+n);
		merge(a[i].u+n,a[i].v);
	}
	if(sf)l==r?(cout<<"Yes\n",0):(dfs(p<<1,l,mid),dfs(p<<1|1,mid+1,r),0);
	else for(int i=l;i<=r;i++)cout<<"No\n";
	for(;st>tmp;st--)f[s1[st]]=s1[st],sz[s2[st]]-=sz[s1[st]];
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>m>>k;
	for(int i=1;i<=2*n;i++)f[i]=i,sz[i]=1;
	for(int i=1,l,r;i<=m;i++)cin>>a[i].u>>a[i].v>>l>>r,l<r?addsum(1,1,k,l+1,r,i),0:0;
	dfs(1,1,k);
}

U535960 【模板】撤销RMQ

题目

Description

已知一个数列 {a
i

},你需要进行下面三种操作:

将某区间每一个数对 x 取 max。
求出某区间所有数的 max。
撤销某一个 1 操作。

posted @ 2025-06-04 08:17  linjingxiang  阅读(142)  评论(0)    收藏  举报