CDQ分治,按照横坐标排序,二分高度,按mid将当前处理的点分成两部分,计算高度>mid作为右上角的对高度≤mid的作为左下角的贡献
横坐标从右往左扫,因为田地内部不能有稻草人,所以对于右上角要求从右往左高度递增,对于左下角要求从右往左高度递增,所以维护两个单调队列
这里要注意几个问题,
对于一个上方的点,如果他比右边的同类点矮,那么要找到他右边第一个比他矮的同类点重新统计对当前左下角的点的贡献
对于一个作为左下角的点,因为他的右边比他高的同类点会挡住一些上方的点,所以计算对他的贡献时,二分找到他右边第一个比他高的同类点,从这个点的左边的第一个上方点开始计算对当前左下角点的贡献
题解后来写的,代码和题解的做法不一样,复杂些,效率比较低下,不推荐参考…
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 210000;
struct node
{
int x,y;
node(){}
node(int _x,int _y){x=_x;y=_y;}
}a[maxn];
int n;
ll re;
bool cmp(node x,node y){return x.x<y.x;}
bool cmp2(node x,node y){return x.y<y.y;}
int q[maxn],q2[maxn];
int tc(int x,int l,int r)
{
while(l<=r)
{
int mid=(l+r)>>1;
if(a[q[mid]].x>x) l=mid+1;
else r=mid-1;
}
return l-1;
}
int tc2(int x,int l,int r)
{
while(l<=r)
{
int mid=(l+r)>>1;
if(a[q2[mid]].y>x) l=mid+1;
else r=mid-1;
}
return l-1;
}
void CDQ(int l,int r)
{
if(l==r) return ;
int mid=(l+r)>>1;
CDQ(l,mid);
CDQ(mid+1,r);
int d=mid,j=r,la=0,ed=n;
int tail=0,tail2=0;
node *x=&a[d];
for(int i=d;i>=l;i--,x--)
{
while(tail2&&(*x).y>a[q2[tail2]].y) tail2--;
while(j>d&&a[j].x>(*x).x)
{
while(tail&&a[j].y<a[q[tail]].y) tail--;
q[++tail]=j;
j--;
}
int t2=tc2((*x).y,1,tail2);
if(t2)
{
t2=tc(a[q2[t2]].x,1,tail);
}
re+=(ll)(tail-t2);
q2[++tail2]=i;
la=(*x).y;
}
sort(a+l,a+r+1,cmp);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x,y; scanf("%d%d",&x,&y);
a[i]=node(x,y);
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++) a[i].x=i;
sort(a+1,a+n+1,cmp2);
for(int i=1;i<=n;i++) a[i].y=i;
re=0ll;
CDQ(1,n);
printf("%lld\n",re);
return 0;
}