线段树进阶

线段树进阶

线段树分治

P5787

判断二分图可以用扩展域并查集,采用线段树分治,线段树上的每一个结点作为每一段时间。

每个结点上的修改和回溯需要用到可撤销的并查集。

时间复杂度 \(O(n\log k \log n)\)

扫描线

扫描线求矩形面积

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+5;
int x[N];
struct node1{
	int xa,xb,y,type;
}line[N];
struct node2{
	int l,r;
	ll sum;
	int cnt;
}tree[N<<3];
bool cmp1(int x,int y){
	return x < y;
}
bool cmp2(node1 x,node1 y){
	return x.y < y.y;
}
void build(int p,int l,int r){
	tree[p].l = x[l];
	tree[p].r = x[r];
	if(r-l<=1){
		return;
	}
	int mid = (l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid,r);
}
void update(int p){
	if(tree[p].cnt){
		tree[p].sum = tree[p].r - tree[p].l;
	}else{
		tree[p].sum = tree[p<<1].sum + tree[p<<1|1].sum;
	}
}
void add(int p,int l,int r,int c){
	if(l <= tree[p].l && r >= tree[p].r){
		tree[p].cnt += c;
		update(p);
		return;
	}
	if(l < tree[p<<1].r){
		add(p<<1,l,r,c);
	}
	if(r > tree[p<<1|1].l){
		add(p<<1|1,l,r,c);
	}
	update(p);
}
int main(){
	int n;
	ll ans = 0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int xa,xb,ya,yb;
		scanf("%d%d%d%d",&xa,&ya,&xb,&yb);
		x[i] = xa;
		x[i+n] = xb;
		line[i] = (node1){xa,xb,ya,1};
		line[i+n] = (node1){xa,xb,yb,-1};
	}
	n <<= 1;
	sort(x+1,x+1+n,cmp1);
	sort(line+1,line+1+n,cmp2);
	build(1,1,n);
	for(int i=1;i<=n;i++){
		ans += tree[1].sum * (line[i].y - line[i-1].y);
		add(1,line[i].xa,line[i].xb,line[i].type);
	}
	printf("%lld",ans);
} 

扫描线求矩形周长

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
int x[N];
struct node1{
	int xa,xb,y,type;
}line[N];
struct node2{
	int l,r,sum,cnt;
	int t;
	bool lc,rc;
}tree[N<<3];
bool cmp1(int x,int y){
	return x < y;
}
bool cmp2(node1 x,node1 y){
	return x.y == y.y ? x.type > y.type : x.y < y.y;
}
void build(int p,int l,int r){
	tree[p].l = x[l];
	tree[p].r = x[r];
	if(r-l<=1){
		return;
	}
	int mid = (l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid,r);
}
void update(int p){
	if(tree[p].cnt){
		tree[p].sum = tree[p].r - tree[p].l;
		tree[p].lc = true;
		tree[p].rc = true;
		tree[p].t = 2;
	}else{
		tree[p].lc = tree[p<<1].lc;
		tree[p].rc = tree[p<<1|1].rc;
		tree[p].t = tree[p<<1].t + tree[p<<1|1].t;
		tree[p].sum = tree[p<<1].sum + tree[p<<1|1].sum;
		if(tree[p<<1].rc && tree[p<<1|1].lc){
			tree[p].t -= 2;
		}
	}
}
void add(int p,int l,int r,int c){
	if(l <= tree[p].l && r >= tree[p].r){
		tree[p].cnt += c;
		update(p);
		return;
	}
	if(l < tree[p<<1].r){
		add(p<<1,l,r,c);
	}
	if(r > tree[p<<1|1].l){
		add(p<<1|1,l,r,c);
	}
	update(p);
}
int main(){
	int n,ans = 0,last = 0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int xa,xb,ya,yb;
		scanf("%d%d%d%d",&xa,&ya,&xb,&yb);
		x[i] = xa;
		x[i+n] = xb;
		line[i] = (node1){xa,xb,ya,1};
		line[i+n] = (node1){xa,xb,yb,-1};
	}
	n <<= 1;
	sort(x+1,x+1+n,cmp1);
	sort(line+1,line+1+n,cmp2);
	build(1,1,n);
	for(int i=1;i<=n;i++){
		add(1,line[i].xa,line[i].xb,line[i].type);
		ans += abs(last-tree[1].sum);
		last = tree[1].sum;
		ans += tree[1].t*(line[i+1].y - line[i].y);
	}
	printf("%d",ans);
} 

线段树合并

线段树分裂

posted @ 2024-02-16 08:41  WhileTureRP++  阅读(21)  评论(0)    收藏  举报