bzoj 4237 稻草人

$cdq$ 分治 + 单调栈.

将所有点先按照 $y$ 排序,然后 $cdq$ 分治,每次只考虑上面的一部分点作为右上角,下面的一部分点作为左下角的贡献.

将上下两部分的点分别按照 $x$ 排序,从左往右枚举上面的点.

发现上面的一个点在只会受到 $y$ 比自己小的点中, $x$ 最大的点的制约,维护一个 $y$ 递增的单调栈来找出这个点.

然后在下面的点中统计有哪些点是合法的,对下面的点维护一个 $y$ 不增的单调栈.

每次在上面加入点后,就将下面 $x$ 小于等于它的点加入下面的栈.

在下面的单调栈中使用二分,找出合法点的区间,时间复杂度 $O(n\log^2 n)$ .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
fh=-1,jp=getchar();
while (jp>='0'&&jp<='9')
out=out*10+jp-'0',jp=getchar();
return out*fh;
}
const int MAXN=2e5+10;
struct node
{
int x,y;
}a[MAXN],up[MAXN],down[MAXN];
bool cmpy(node a,node b)
{
return a.y==b.y?a.x<b.x:a.y>b.y;
}
bool cmpx(node a,node b)
{
return a.x==b.x?a.y<b.y:a.x<b.x;
}
int n,stk1[MAXN],stk2[MAXN],tp1,tp2;
ll ans=0;
void cdq(int l,int r)
{
if(l==r)
return;
int mid=(l+r)>>1;
cdq(l,mid);
tp1=tp2=0;
int ls=mid-l+1,rs=r-mid;
for(int i=l;i<=mid;++i)
up[i-l+1]=a[i];
for(int i=mid+1;i<=r;++i)
down[i-mid]=a[i];
sort(up+1,up+1+ls,cmpx);
sort(down+1,down+1+rs,cmpx);
int p=1;
for(int i=1;i<=ls;++i)
{
while(tp1>0 && up[stk1[tp1]].y>=up[i].y)
--tp1;
stk1[++tp1]=i;
while(p<=rs && down[p].x<=up[i].x)
{
while(tp2>0 && down[stk2[tp2]].y<down[p].y)
--tp2;
stk2[++tp2]=p;
++p;
}
if(!tp2)
continue;
if(tp1==1)
{
ans+=tp2;
continue;
}
int L=1,R=tp2,lim=up[stk1[tp1-1]].x,res=-1;
while(L<=R)
{
int mid=(L+R)>>1;
if(down[stk2[mid]].x>=lim)
res=mid,R=mid-1;
else
L=mid+1;
}
if(res!=-1)
ans+=tp2-res+1;
}
cdq(mid+1,r);
}
int main()
{
n=read();
for(int i=1;i<=n;++i)
{
a[i].x=read();
a[i].y=read();
}
sort(a+1,a+1+n,cmpy);
cdq(1,n);
cout<<ans<<endl;
return 0;
}