1. 程式人生 > 其它 >LOJ #2880,BZOJ 4237,LG AT1225 「JOISC 2014 Day3」稻草人

LOJ #2880,BZOJ 4237,LG AT1225 「JOISC 2014 Day3」稻草人

かかしかかしかかしかかしかかしかかしかかしかかしかかしかかしかかし

如果不用關心組成的矩形裡面有沒有其他稻草人,就可以直接二維數點了,可惜不行,那麼我們考慮cdq分治。(我也不知道為什麼是這樣轉化)
不妨按y分治,最後的陣列是歸併排序成 \(x\) 升序的。

如果在同一個邊,那就繼續分治
如果不在同一邊,怎麼計算?
不妨在掃右上角的點的時候統計答案。
設當前掃到的右上角的點(在上半邊)是 \((x_b,y_b)\) 合法的左下角點(在下半邊)是 \(x_a,y_a\)
我們最後的陣列是歸併成 \(x\) 升序的,所以我們掃到的點都是 \(x\) 遞增的。
一對點對受到了兩種限制,上半邊和下半邊。

  • 上半邊的限制
    \(x_a<x_c<x_b,y_a<y_c<y_b\)\((x_a,y_a)\) 不能存在。(\(a,b\) 組成的矩形中間有點)
    我們掃過的所有點正是 \(y_a<y_c,x_c<x_b\) 的所有點(\(b\) 左下的點),那麼就是要求 \(\forall y_c<y_b,x_a> x_c\)
    也就是 \(x_a \ge max\{x_c | y_c<y_b\}\)。(圖中,如果以橙點為 \(b\),那麼由於粉點的影響,\(a\) 可以是藍點,但不可以是綠點。)
    所以維護一個 \(y\) 單調遞增的單調棧,用於找到左邊最近的(即 \(x_c\)
    最大) \(y_c<y_b\) 就可以了。
  • 下半邊的限制
    \(x_a<x_c<x_b,y_a<y_c<y_b\)\((x_a,y_a)\) 不能存在。(\(a,b\) 組成的矩形中間有點)
    我們掃過的所有點正是 \(y_a<y_c,x_c<x_b\) 的所有點(\(b\) 左下的點),那麼就是要求 \(\forall x_a<x_c,y_a>y_c\)
    其實就是任意滿足 \(x_1<x_2\) 兩點的,必須同時滿足 \(y_1>y_2\)(圖中,如果以紫點為 \(b\),那麼\(a\) 可以是灰點,但不可以是綠點和藍點。)
    所以維護一個 \(y\)
    單調遞減的單調棧。棧內就是 \(x\) 遞增,\(y\) 遞減的了。

然後每次統計答案的時候,二分單調棧即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls(x) ((x)<<1)
#define rs(x) ((x)<<1|1)
#define fi first
#define se second
#define mkp make_pair
#define PII pair<int,int>
int read() {
    char c = getchar(); int ff = 1; int x = 0;
    while(c < '0' || c > '9') { if(c == '-') ff = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return ff * x;
}
const int N = 2e5 + 10;
int n, lshx[N], lshy[N], lenx, leny; ll ans;
struct node {
	int x, y;
	friend bool operator < (node &a, node &b) {
		return a.y < b.y;
	} 
}p[N], tmp[N];
int st1[N], st2[N];
bool cmp(int a, int b) { return p[a].x < p[b].x; }
void cdq(int l, int r) {
	
	if(l == r) return;
	int mid = (l + r) >> 1;
	cdq(l, mid); cdq(mid + 1, r);
	//st1:x單調遞增,維護y單調遞減。(如果該點能貢獻,那麼在下半部分內它右上角且橫座標x之前的地方,沒點。即任意貢獻點,x1<x2,y1>y2) 
	//st2: x單調遞增,找左邊第一個滿足y'<y的,維護y單調遞增。 
	int i = l, j = mid + 1, k = l - 1, top1 = 0, top2 = 0;
	while(i <= mid || j <= r) {
		if(j > r || (i <= mid && p[i].x < p[j].x)) {
			while(top1 && p[st1[top1]].y < p[i].y) top1--;
			tmp[++k] = p[i]; st1[++top1] = i; i++;
		} else {
			while(top2 && p[st2[top2]].y > p[j].y) top2--; 
			
			p[n + 1].x = p[n + 1].y = 0; if(top2) p[n + 1].x = p[st2[top2]].x;
			int pos = lower_bound(st1 + 1, st1 + top1 + 1, n + 1, cmp) - st1;
			ans += top1 - pos + 1; 
			tmp[++k] = p[j]; st2[++top2] = j; j++;
		}
	}
	for(int i = l; i <= r; i++) p[i] = tmp[i];

	return;
}
int main() {
//	freopen("ex.out", "w", stdout);
	n = read();
	for(int i = 1; i <= n; i++) {
		p[i].x = read(), p[i].y = read();
		lshx[++lenx] = p[i].x, lshy[++leny] = p[i].y;
	}
	sort(lshx + 1, lshx + lenx + 1); sort(lshy + 1, lshy + leny + 1);
	for(int i = 1; i <= n; i++) {
		p[i].x = lower_bound(lshx + 1, lshx + lenx + 1, p[i].x) - lshx;
		p[i].y = lower_bound(lshy + 1, lshy + leny + 1, p[i].y) - lshy;
	}
	sort(p + 1, p + n + 1);
	cdq(1, n);
	printf("%lld\n", ans);
	return 0;
}