AcWing 1228 油漆面积 题解(蓝桥杯 线段树)

算法思想:就是将每个矩形的竖边依附于一条竖线,将这条竖线加入线段树中,并不断遍历这些竖线,利用线段树更新竖线上被矩形覆盖的长度,tr[1]记录的是当前竖线上被覆盖的总长度,len*记录的x之差就是这块矩形的面积,遍历所有竖线之后,就可以得出所有被矩形覆盖的面积
原题
大佬题解

#include

using namespace std;

const int N = 1e4 + 10;

struct Segment
{
    int x, y1, y2; //x为横坐标,y1,y2为线段树维护的两个纵坐标
    int k; //k表示我们要加的权值,为+1或-1
    //由于要把所有的线段按照横坐标排序,所以要重载小于号
    bool operator< (const Segment &t)const
    {
        return x < t.x;
    }
}seg[N * 2]; //线段最多有两倍(每个矩形两条竖边)

struct Node{
	int l, r;
	int cnt;//区间被覆盖的次数 
	int len;//这条竖线上至少被覆盖一次的区间总长度
}tr[N * 4];

void pushup(int u){
	if(tr[u].cnt > 0) tr[u].len = tr[u].r - tr[u].l + 1;//多次被更新的时候,代表这条线是被重复覆盖的,所以直接左右边界相减即可 
	else if(tr[u].l == tr[u].r) tr[u].len = 0;//默认线段树的叶节点长度为为0 
	else tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;//第一次更新的时候记录长度 
}

void build(int u, int l, int r){//建树 
	tr[u] = {l, r, 0, 0};//记录这条竖边的上下边界,cnt和len默认为0 
	if(l == r) return ;//递归到线段树的子节点就返回 
	int mid = l + r >> 1;
	build(u << 1, l, mid);
	build(u << 1 | 1, mid + 1, r);
}

void modify(int u, int l, int r, int k){//遇到一条新的矩形竖边,就再更新一次 
	//当前区间已经被完全包含
	if(l <= tr[u].l && r >= tr[u].r){ 
		tr[u].cnt += k;//更新被覆盖的次数
		pushup(u); 
	}
	else{//往下寻找 
		int mid = tr[u].l + tr[u].r >> 1;
		if(l <= mid) modify(u << 1, l, r, k);
		if(r > mid) modify(u << 1 | 1, l, r, k);
		pushup(u); 
	}
}

int n;

int main()
{
	scanf("%d", &n); 
	int m = 0;
	for(int i = 0; i < n; i ++ ){
		int x1, y1, x2, y2;
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		seg[m ++ ] = {x1, y1, y2, 1};//矩形左竖边,1表示矩形来了 
		seg[m ++ ] = {x2, y1, y2, -1}; //矩形右竖边,-1表示矩形离开 
	}
	
	sort(seg, seg + m);//给所有竖线按照x坐标排序
	
	build(1, 0, 10000);
	int ans = 0;
	for(int i = 0; i < m; i ++ ){ //遍历所有竖线 
		if(i > 0){//第一条竖边无法计算面积 
			ans += tr[1].len * (seg[i].x - seg[i - 1].x);//根结点的len * 阴影部分高度
		}
		modify(1, seg[i].y1, seg[i].y2 - 1, seg[i].k);//维护的是一小段区间
	}
	
	cout<<ans<<endl;
	
	return 0;
}

你可能感兴趣的:(蓝桥杯,#,线段树,算法,蓝桥杯)