1. 程式人生 > >【CodeForces - 983D】Arkady and Rectangles

【CodeForces - 983D】Arkady and Rectangles

@Arkady and [email protected]


@Description - [email protected]

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 - [email protected]

給一個二維平面上塗顏色。每次塗一個完整的矩形。後塗的會覆蓋掉先塗的。

問這樣塗了 n 種顏色後,有幾種顏色可以被看見。( n <= 10^5 )

注:平面無限大,且一開始覆蓋著白色。

@[email protected]

演算法略暴力的一道題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。

離散化需要把某個座標周圍的點加入,否則好像會出問題的樣子。
【沒想清楚,請看到的好心人提醒博主仔細想想這個問題】

@[email protected]

用 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);
}

@[email protected]

就是這樣,新的一天裡,也請多多關照哦(ノω<。)ノ))☆.。