【bzoj4237】稻草人 CDQ分治

       可能严格来讲不算CDQ分治把。。我也不太清楚>_<。

       按照y坐标来分治,然后首先处理上下两块(下面的y坐标小)的答案。

       然后对于某一段,首先按照x坐标进行排序,考虑上面的一块对下面一块产生的答案。考虑上面一块中的某一个点i,以它为右上角的矩形的左下角的x坐标是有范围的限制的(左下角在下面一块中)。显然左下角的x坐标不能<第一个在上面的离i最近的y坐标比i小的点的x坐标(有点拗口233),然后左下角的点也是有限制的,也就是最为左下角的点如果有两个点i,j使得x[i]<x[j]且y[i]<y[j],那么i是不满足的。

       于是直接将上面一块的点维护一个y坐标单调递增的栈,下面的维护一个y坐标单调递减的栈,然后二分查找一下即可。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200005
#define ll long long
using namespace std;

int n,m,p[N],q[N]; struct node{ int x,y; }a[N],b[N]; ll ans;
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
bool cmpy(node u,node v){ return u.y<v.y; }
int find(int x,int l,int r){
	while (l+1<r){
		int mid=(l+r)>>1;
		if (a[q[mid]].x<x) l=mid; else r=mid;
	}
	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 tp1=0,tp2=0,i,j=l,k;
	for (i=mid+1; i<=r; i++){
		while (tp1 && a[i].y<a[p[tp1]].y) tp1--;
		p[++tp1]=i;
		for (; a[j].x<a[i].x && j<=mid; j++){
			while (tp2 && a[j].y>a[q[tp2]].y) tp2--;
			q[++tp2]=j;
		}
		ans+=tp2-find(a[p[tp1-1]].x,0,tp2+1);
	}
	j=l; k=mid+1;
	for (i=l; i<=r; i++)
		b[i]=(j<=mid && a[j].x<a[k].x || k>r)?a[j++]:a[k++];
	for (i=l; i<=r; i++) a[i]=b[i];
}
int main(){
	n=read(); int i;
	for (i=1; i<=n; i++){
		a[i].x=read(); a[i].y=read();
	}
	sort(a+1,a+n+1,cmpy);
	solve(1,n); printf("%lld\n",ans);
	return 0;
}


by lych

2016.3.18

你可能感兴趣的:(分治,二分,cdq分治)