1. 程式人生 > 實用技巧 >聯賽模擬測試21 表格

聯賽模擬測試21 表格

挺神仙的一道題,考場上打的30分的思路,作死沒打線段樹打的珂朵莉樹,然後和暴力一個分......(其實考完後發現改過來也只得了十分,啊我內心平衡了.)
聽說這道題四分樹(二維線段樹)可以得到不少的分,上一屆學長不少都是\(O(n^2)\)碾過去的。

這裡來說一下正解吧,個人感覺做這道題最起碼要會掃描線

借用掃描線的思路,我們可以考慮在每一個線段樹的節點裡面維護什麼東西,我們給每種顏色以其出現的先後順序編號,由於這道題的編號大的顏色會覆蓋編號小的顏色,我們需要知道該區間之中還沒有計入答案的最大的顏色\(maxv\),當前區間可以看到的最小的顏色\(minv\),如果後者大於前者,這個顏色顯然是無法記錄的.同時為了動態維護當前完全覆蓋當前區間的所有顏色以更新前面的兩個東西,對於每個節點我們需要開一個\(set\)

,記錄完全覆蓋當前區間的所有顏色,於是我們可以獲得更新他們的方法.

前者好理解,後者就是現在兩個子樹裡面取\(min\),但是如果這種顏色沒有完全覆蓋這個區間的顏色的最大值大的話就會被覆蓋了.這樣就完成了更新.
完成了上述維護之後就好說了,先掃描線常規操作離散化一下,再以此取出根節點可以看到的尚未被標記的最大的顏色,標記之後再次向下更新,掃描完之後統計一下被標記的一共有幾種顏色就可以了.(嚴重譴責學長下發的\(std\),根本看不懂\(QAQ\))



#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#define sit std::set<int>::iterator
#define lson (t << 1)
#define rson (t << 1 | 1)
#define mid ((l + r) >> 1)
const int N = 5e6 + 10;
struct Node {
	int x, up, down, id, pos;
	bool operator < (const Node &a)const {
		return a.x > x;
	}
} a[N];
int num[N], top, tag[N];
using std::lower_bound;
struct Tree { //維護每一個線段樹節點
	int maxv, minv;
	std::set<int> s;
} tree[N];
void build(int t, int l, int r) {
	tree[t].maxv = -1;//-1表示區間裡面看不到未標記的顏色
	if(l == r) return;
	build(lson, l, mid);
	build(rson, mid + 1, r);
}
void pushup(int t) {//按照上式更新,注意set是否為空
	tree[t].maxv = std::max(tree[lson].maxv, tree[rson].maxv);
	if(!tree[t].s.empty()) tree[t].maxv = std::max(tree[t].maxv, *--tree[t].s.end());
	tree[t].minv = std::min(tree[lson].minv, tree[rson].minv);
	if(!tree[t].s.empty()) tree[t].minv = std::max(tree[t].minv, *--tree[t].s.end());
	if(tree[t].minv > tree[t].maxv) {//被覆蓋
		tree[t].maxv = -1;
	}
	else if(tag[tree[t].maxv]) tree[t].maxv = -1;//看到過
}
void Add(int t, int l, int r, int cl, int cr, int num) {//加入新顏色
	if(cl <= l && r <= cr) {
		tree[t].s.insert(num);
		pushup(t);
		return;
	}
	if(cr <= mid) Add(lson, l, mid, cl, cr, num);
	else if(cl > mid) Add(rson, mid + 1, r, cl, cr, num);
	else {
		Add(lson, l, mid, cl, cr, num);
		Add(rson, mid + 1, r, cl, cr, num);
	}
	pushup(t);
}
void Erase(int t, int l, int r, int el, int er, int num) {//去掉老顏色
	if(el <= l && r <= er) {
		sit it = tree[t].s.lower_bound(num);
		tree[t].s.erase(it);
		pushup(t);
		return;
	}
	if(er <= mid) Erase(lson, l, mid, el, er, num);
	else if(el > mid) Erase(rson, mid + 1, r, el, er, num);
	else {
		Erase(lson, l, mid, el, er, num);
		Erase(rson, mid + 1, r, el, er, num);
	}
	pushup(t);
}
void update(int t, int l, int r, int ul, int ur) {//每標記完一個顏色之後更新一下
	if(ul <= l && r <= ur) {
		pushup(t);
		return;
	}
	if(ur <= mid) update(lson, l, mid, ul, ur);
	else if(ul > mid) update(rson, mid + 1, r, ul, ur);
	else {
		update(lson, l, mid, ul, ur);
		update(rson, mid + 1, r, ul, ur);
	}
	pushup(t);
}
int L[N], R[N];
int main() {
	freopen("excel.in", "r", stdin);
	freopen("excel.out", "w", stdout);
	int n;
	scanf("%d", &n);
	tag[0] = 1;
	for(int i = 1; i <= n; ++i) {
		int x, y, aa, bb;
		scanf("%d%d%d%d", &x, &y, &aa, &bb);
		a[i * 2 - 1] = (Node){x, bb - 1, y, i, 1};
		a[i * 2] = (Node){aa, bb - 1, y, i, -1};
		num[++top] = y;
		num[++top] = bb - 1;
	}
	std::sort(a + 1, a + n * 2 + 1);
	std::sort(num + 1, num + top + 1);
	top = std::unique(num + 1, num + top + 1) - num - 1;
	build(1, 1, top);
	for(int i = 1; i <= n * 2; ++i) {
		int up = lower_bound(num + 1, num + top + 1, a[i].up) - num;
		int down = lower_bound(num + 1, num + top + 1, a[i].down) - num;
		if(a[i].pos == 1) {
			Add(1, 1, top, down, up, a[i].id);
			L[a[i].id] = down;
			R[a[i].id] = up;
		}
		else {
			Erase(1, 1, top, down, up, a[i].id);
		}
		if(a[i].x != a[i + 1].x) {
			while(tree[1].maxv != -1) {
				tag[tree[1].maxv] = 1;
				update(1, 1, top, L[tree[1].maxv], R[tree[1].maxv]);
			}
		}
	}
	int ans = 0;
	for(int i = 0; i <= n; ++i) {//統計答案
		if(tag[i]) {
			ans++;
		}
	}
	printf("%d\n", ans);
	return 0;
}