[BZOJ4237]稻草人

BZOJ

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\n",ans);
	return 0;
}
posted @ 2018-02-06 20:16  租酥雨  阅读(188)  评论(0编辑  收藏  举报