【BZOJ4237】稻草人 [分治][单调栈]
稻草人
Time Limit: 40 Sec Memory Limit: 256 MB[Submit][Status][Discuss]
Description
JOI村有一片荒地,上面竖着N个稻草人,村民们每年多次在稻草人们的周围举行祭典。
有一次,JOI村的村长听到了稻草人们的启示,计划在荒地中开垦一片田地。和启示中的一样,田地需要满足以下条件:
田地的形状是边平行于坐标轴的长方形;
左下角和右上角各有一个稻草人;
田地的内部(不包括边界)没有稻草人。
给出每个稻草人的坐标,请你求出有多少遵从启示的田地的个数
Input
第一行一个正整数N,代表稻草人的个数
接下来N行,第i行(1<=i<=N)包含2个由空格分隔的整数Xi和Yi,表示第i个稻草人的坐标
Output
输出一行一个正整数,代表遵从启示的田地的个数
Sample Input
4
0 0
2 2
3 4
4 3
0 0
2 2
3 4
4 3
Sample Output
3
HINT
1<=N<=2*10^5
0<=Xi<=10^9(1<=i<=N), Xi(1<=i<=N)互不相同。
0<=Yi<=10^9(1<=i<=N), Yi(1<=i<=N)互不相同。
Solution
O(n^2)做法很显然,既然这样,我们就使用惯用套路,我们先对 y 进行分治,将上面的点视为右上角的点,下面的视为左下角的点,统计答案。
首先把两部分的点分别按照 x 升序排序。
然后枚举上面的每个点。
显然,约束到它拓展的是 在它左下方最接近的点。
同时,下面的点最近的右上方点约束到点的拓展。
那我们对于上面维护一个 y 递增的单调栈,对下面维护一个 y 递减的单调栈。
枚举到上面的点的时候,把 x 小于它的下面的点加入下面的那个单调栈,然后二分一下可行的位置就可以了。
(显然,只有当下面的x > 上面单调栈倒数第二个点的 x 的时候 才可以被加入答案)
(middle写成了mid调了一个小时!好气呀(╯‵□′)╯︵┻━┻)
Code
1 #include<iostream> 2 #include<string> 3 #include<algorithm> 4 #include<cstdio> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cmath> 8 #include<queue> 9 using namespace std; 10 typedef long long s64; 11 12 const int ONE = 1000005; 13 14 int get() 15 { 16 int res = 1, Q = 1; char c; 17 while( (c = getchar()) < 48 || c > 57) 18 if(c == '-') Q = -1; 19 if(Q) res = c - 48; 20 while( (c = getchar()) >= 48 && c <= 57) 21 res = res * 10 + c - 48; 22 return res * Q; 23 } 24 25 int n; 26 27 struct point 28 { 29 int x, y; 30 }a[ONE]; 31 32 bool cmpx(const point &a, const point &b) {return a.x < b.x;} 33 bool cmpy(const point &a, const point &b) {return a.y < b.y;} 34 35 int Stk_down[ONE], Stk_up[ONE]; 36 s64 Ans; 37 38 void Solve(int l, int r) 39 { 40 if(l >= r) return; 41 int mid = l + r >> 1; 42 43 sort(a + l, a + r + 1, cmpy); 44 sort(a + l, a + mid + 1, cmpx); 45 sort(a + mid + 1, a + r + 1, cmpx); 46 47 int top_up = 0, top_down = 0; 48 int now = l; 49 50 for(int i = mid + 1; i <= r; i++) 51 { 52 while(top_up > 0 && a[Stk_up[top_up]].y >= a[i].y) top_up--; 53 Stk_up[++top_up] = i; 54 55 while(now <= mid && a[now].x <= a[i].x) 56 { 57 while(top_down > 0 && a[Stk_down[top_down]].y <= a[now].y) top_down--; 58 Stk_down[++top_down] = now; 59 now++; 60 } 61 62 int left = 1, right = top_down, pos = 0; 63 int lx = top_up - 1 > 0 ? a[Stk_up[top_up - 1]].x : -1; 64 65 while(left < right - 1) 66 { 67 int middle = left + right >> 1; 68 if(a[Stk_down[middle]].x >= lx) 69 right = middle; 70 else 71 left = middle; 72 } 73 74 75 if(a[Stk_down[left]].x >= lx) pos = left; 76 else 77 if(a[Stk_down[right]].x >= lx) pos = right; 78 79 if(pos) Ans += top_down - pos + 1; 80 } 81 82 Solve(l, mid), Solve(mid + 1, r); 83 } 84 85 int main() 86 { 87 n = get(); 88 for(int i = 1; i <= n; i++) 89 a[i].x = get(), a[i].y = get(); 90 91 Solve(1, n); 92 printf("%lld", Ans); 93 }