【CodeForces - 983D】Arkady and Rectangles

版权声明:本文为博主原创文章……懂吗?要尊重别人的劳动成果呐 https://blog.csdn.net/Tiw_Air_Op1721/article/details/84038246


@Description - English@

Arkady has got an infinite plane painted in color 0. Then he draws n rectangles filled with paint with sides parallel to the Cartesian coordinate axes, one after another. The color of the i-th rectangle is i (rectangles are enumerated from 1 to n in the order he draws them). It is possible that new rectangles cover some of the previous ones completely or partially.

Count the number of different colors on the plane after Arkady draws all the rectangles.

Input
The first line contains a single integer n (1≤n≤100000) — the number of rectangles.

The i-th of the next n lines contains 4 integers x1, y1, x2 and y2 (−109≤x1<x2≤109, −109≤y1<y2≤109) — the coordinates of corners of the i-th rectangle.

Output
In the single line print the number of different colors in the plane, including color 0.

Examples
input
5
-1 -1 1 1
-4 0 0 4
0 0 4 4
-4 -4 0 0
0 -4 4 0
output
5

input
4
0 0 4 4
-4 -4 0 0
0 -4 4 0
-2 -4 2 4
output
5

Note
That’s how the plane looks in the first sample
题目描述图1
That’s how the plane looks in the second sample
题目描述图2
0 = white, 1 = cyan, 2 = blue, 3 = purple, 4 = yellow, 5 = red.

@Decription - Chinese@

给一个二维平面上涂颜色。每次涂一个完整的矩形。后涂的会覆盖掉先涂的。

问这样涂了 n 种颜色后,有几种颜色可以被看见。( n <= 10^5 )

注:平面无限大,且一开始覆盖着白色。

@Solution@

算法略暴力的一道题qwq。

为了把这个二维问题降为一维的,我们考虑 离线 + 扫描线 ,并用一个线段树维护扫描线。

怎么维护呢?先给每种颜色编号,为涂上颜色的次序。编号大的颜色会覆盖编号小的。然后,对于当前的扫描线,我们找出一个能被看到的颜色,把这个颜色打上标记;再找一个没有被打上标记的,能被看到的颜色,把它打上标记……重复若干次,直到当前扫描线上所有的颜色都被打上标记后,向前移动扫描线。

然后就是怎么实现的问题:
显然编号越大越容易被看见。所以对于线段树上的每个区间,我们定义 mx 表示这个区间能被看到的,编号最大的,没有被打上标记的颜色编号。为了方便维护 mx,我们再给每个区间定义 mn 表示这个区间能被看到的,编号最小的颜色编号;(最暴力的一步)定义一个 set 存储能够完全覆盖这个区间的颜色集合。

mx 可以这样维护: m x = max ( s e t m a x , max ( m x l e f t , m x r i g h t ) ) mx = \max(set_{max}, \max(mx_{left}, mx_{right}))
因为 set 中的最大颜色会覆盖掉 set 中的其他颜色,所以可以直接取最大值。
如果这个 set 中的最大颜色比左右子树中的最大颜色都大,则这个区间就肯定只能看到这一种颜色;否则就可以取左右子树中的较大值。
如果发现这个 mx 已经标记过了,则令 mx = -1;这样就不会影响上一层的答案(因为是取较大值,而 -1 比所有颜色编号都小的,故不会被取)。

mn 可以这样维护: m n = max ( s e t m a x , min ( m n l e f t , m n r i g h t ) ) mn = \max(set_{max}, \min(mn_{left}, mn_{right}))
具体解释和 mx 相差不远。
mn 用来处理这一情况:某一个颜色 C 完全覆盖了某一区间,然而一些零零散散的区间接起来可以把颜色 C 覆盖掉,则我们需要排除 C,然而上面的过程会将 C 标记。
具体到代码,即:我们如果发现 mx < mn,就将 mx 也赋值为 -1。

离散化需要把某个坐标周围的点加入,否则好像会出问题的样子。
【没想清楚,请看到的好心人提醒博主仔细想想这个问题】

@Code@

用 set 真的也……太暴力了吧。
但是时间竟然还是O(nlog^2n)的。

#include<set>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int INF = (1<<30);
const int MAXN = 100000;
struct node{
	int le, ri;
	set<int>Set;
	int mx, mn;
}tree[24*MAXN + 5];
bool tag[MAXN + 5];
void maintain(int x) {
	if( tree[x].le == tree[x].ri )
		tree[x].mx = tree[x].mn = *tree[x].Set.rbegin();
	else {
		tree[x].mn = max(*tree[x].Set.rbegin(), min(tree[x<<1].mn, tree[x<<1|1].mn));
		printf("%d\n", *tree[x].Set.rbegin());
		tree[x].mx = max(*tree[x].Set.rbegin(), max(tree[x<<1].mx, tree[x<<1|1].mx));
	}
	if( tree[x].mx < tree[x].mn ) tree[x].mx = -1;
	if( tree[x].mx != -1 && tag[tree[x].mx] ) tree[x].mx = -1;
}//rbegin() -> 取出set中的最大值 
//理论上可以用优先队列实现,但是我太懒了 qwq 
void build(int x, int l, int r) {
	tree[x].le = l, tree[x].ri = r;
	tree[x].mx = tree[x].mn = 0;
	tree[x].Set.clear(); tree[x].Set.insert(0); //防止集合为空而 RE 
	if( l == r ) return ;
	int mid = (l + r) >> 1;
	build(x<<1, l, mid);
	build(x<<1|1, mid+1, r);
}
void modify(int x, int l, int r, int k, int type) {
	if( l > tree[x].ri || r < tree[x].le )
		return ;
	if( l <= tree[x].le && tree[x].ri <= r ) {
		if( type ) tree[x].Set.insert(k);
		else tree[x].Set.erase(k);
		maintain(x);
		return ;
	}
	modify(x<<1, l, r, k, type);
	modify(x<<1|1, l, r, k, type);
	maintain(x);
}
void update(int x, int l, int r) {
	if( l > tree[x].ri || r < tree[x].le )
		return ;
	if( l <= tree[x].le && tree[x].ri <= r ) {
		maintain(x);
		return ;
	}
	update(x<<1, l, r);
	update(x<<1|1, l, r);
	maintain(x);
}
int N;
int Xa[MAXN + 5], Xb[MAXN + 5], Ya[MAXN + 5], Yb[MAXN + 5];
int dct[2][6*MAXN + 5], dcnt[2];
void discrete() {
	for(int i=0;i<2;i++)
		sort(dct[i]+1, dct[i]+dcnt[i]+1);
	for(int i=0;i<2;i++)
		dcnt[i] = unique(dct[i]+1, dct[i]+dcnt[i]+1) - dct[i] - 1;
	for(int i=1;i<=N;i++) {
		Xa[i] = lower_bound(dct[0]+1, dct[0]+dcnt[0]+1, Xa[i]) - dct[0];
		Xb[i] = lower_bound(dct[0]+1, dct[0]+dcnt[0]+1, Xb[i]) - dct[0];
		Ya[i] = lower_bound(dct[1]+1, dct[1]+dcnt[1]+1, Ya[i]) - dct[1];
		Yb[i] = lower_bound(dct[1]+1, dct[1]+dcnt[1]+1, Yb[i]) - dct[1];
	}
}
void insert_dct(int val, int type) {
	dct[type][++dcnt[type]] = val;
}
struct query{
	int le, ri, num;
	bool type;
	query(int _l=0, int _r=0, int _n=0, bool _t=0):le(_l), ri(_r), num(_n), type(_t){}
};
vector<query>vec[6*MAXN + 5];
int main() {
	scanf("%d", &N);
	for(int i=1;i<=N;i++) {
		scanf("%d%d%d%d", &Xa[i], &Ya[i], &Xb[i], &Yb[i]);
		Xb[i]--, Yb[i]--;
		insert_dct(Xa[i], 0); insert_dct(Xa[i]+1, 0); insert_dct(Xa[i]-1, 0);
		insert_dct(Xb[i], 0); insert_dct(Xb[i]+1, 0); insert_dct(Xb[i]-1, 0);
		insert_dct(Ya[i], 1); insert_dct(Ya[i]+1, 1); insert_dct(Ya[i]-1, 1);
		insert_dct(Yb[i], 1); insert_dct(Yb[i]+1, 1); insert_dct(Yb[i]-1, 1);
	}
	discrete();
	for(int i=1;i<=N;i++) {
		vec[Xa[i]].push_back(query(Ya[i], Yb[i], i, 1));
		vec[Xb[i]+1].push_back(query(Ya[i], Yb[i], i, 0));
	}
	build(1, 1, dcnt[1]); Ya[0] = 1; Yb[0] = dcnt[1]; //0也在线段树中 
	for(int i=1;i<=dcnt[0];i++) {
		for(int j=0;j<vec[i].size();j++)
			modify(1, vec[i][j].le, vec[i][j].ri, vec[i][j].num, vec[i][j].type);
		while( tree[1].mx != -1 ) {
			tag[tree[1].mx] = true;
			update(1, Ya[tree[1].mx], Yb[tree[1].mx]);
		}
	}
	int ans = 0;
	for(int i=0;i<=N;i++)
		if( tag[i] ) ans++;
	printf("%d\n", ans);
}

@End@

就是这样,新的一天里,也请多多关照哦(ノω<。)ノ))☆.。

猜你喜欢

转载自blog.csdn.net/Tiw_Air_Op1721/article/details/84038246