sol
首先按横坐标排序。考虑CDQ,即考虑左边的点对右边的点的贡献。
可以发现,左边的点可以对右边的点做贡献的只有上凸壳上面的点
按照y坐标排序,如果是左边的点就更新上凸壳,如果是右边的点就在上凸壳上面二分,计算产生了多少的贡献。
但是右边的点可能会对右边的点产生阻挡。
可以对右边的点也维护一个横坐标递增的单调队列,每次在上凸壳上二分找的时候相当于有了一个界限。
code
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 200005;
#define ll long long
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
int n,q1[N],q2[N];ll ans;
struct node{
int x,y;
bool operator < (const node &b) const
{return x<b.x;}
}a[N],b[N];
int find(int val,int l,int r)
{
while (l<r)
{
int mid=l+r+1>>1;
if (a[q2[mid]].y<val) l=mid;
else r=mid-1;
}
return l;
}
void solve(int l,int r)
{
if (l==r) return;
int mid=l+r>>1;
solve(l,mid);solve(mid+1,r);
int t1=0,t2=0;
for (int i=mid+1,j=l;i<=r;i++)
{
while (t1&&a[i].x<a[q1[t1]].x) --t1;//x坐标单调增
q1[++t1]=i;
for (;a[j].y<a[i].y&&j<=mid;j++)
{
while (t2&&a[j].x>a[q2[t2]].x) --t2;//x坐标单调减
q2[++t2]=j;
}
ans+=t2-find(a[q1[t1-1]].y,0,t2);
}
for (int i=l,j=mid+1,k=l;k<=r;)
b[k++]=(i<=mid&&(j>r||a[i].y<a[j].y))?a[i++]:a[j++];
for (int i=l;i<=r;i++) a[i]=b[i];
}
int main()
{
n=gi();
for (int i=1;i<=n;i++)
a[i]=(node){gi()+1,gi()+1};
sort(a+1,a+n+1);
solve(1,n);
printf("%lld
",ans);
return 0;
}