BZOJ 4237 稻草人(cdq分治+单调栈)

本文介绍了一种基于CDQ分治算法解决特定几何问题的方法。该问题要求在平面上找到满足特定条件的矩形区域数量,即每个区域内恰好有两个指定点作为矩形的左下角和右上角,且区域内不含其他点。文章详细阐述了解决方案的思路、步骤及其实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

JOI村有一片荒地,上面竖着 N N N个稻草人,村民们每年多次在稻草人们的周围举行祭典。 有一次,JOI村的村长听到了稻草人们的启示,计划在荒地中开垦一片田地。和启示中的一样,田地需要满足以下条件: 田地的形状是边平行于坐标轴的长方形; 左下角和右上角各有一个稻草人; 田地的内部(不包括边界)没有稻草人。 给出每个稻草人的坐标,请你求出有多少遵从启示的田地的个数。

输入格式

一行一个整数 N N N,代表稻草人的个数

接下来 N N N行,第 i i i行包含 2 2 2个由空格分隔的整数 x i x_i xi y i y_i yi,表示第 i i i个稻草人的坐标

输出格式

输出一行一个正整数,代表遵从启示的田地的个数

样例输入
4
0 0
2 2
3 4
4 3
样例输出
3
样例解释

所有满足题意的田地如图所示:

在这里插入图片描述

数据范围
  • 1 ≤ N ≤ 2 × 1 0 5 1\leq N\leq 2\times 10^5 1N2×105

  • 0 ≤ x i , y i ≤ 1 0 9 ( 1 ≤ i ≤ N ) 0\leq x_i,y_i\leq 10^9(1\leq i\leq N) 0xi,yi109(1iN)

  • x i x_i xi互不相同, y i y_i yi互不相同

题解

前置知识: c d q cdq cdq分治

题目大意就是在一个平面上有若干个点,求有多少个区域,满足区域左下角和右上角各有一个点,区域内没有点(不包括边界)。因为题目满足: ∀ i , j ( i ≠ j ) , x i ≠ x j , y i ≠ y j \forall i,j(i\neq j),x_i\neq x_j, y_i\neq y_j i,j(i=j),xi=xj,yi=yj,所以只如果左下角和右上角有点,那区间的边界就不会有点。我们就不用特殊考虑边界,直接做就行了。

首先对 x x x值进行排序,然后 c d q cdq cdq分治。对于区间 [ l , r ] [l,r] [l,r],我们让左区间中的点作左下角的点,右区间的点做右上角的点。因为已经按 x x x值排好了序,所以左区间的 x x x值都小于右区间的 x x x值。分别对左右区间的 y y y值进行排序,左区间 x x x值仍小于右区间 x x x值。

对于左区间的点 i , j i,j i,j,如果 x i < x j x_i<x_j xi<xj y i < y j y_i<y_j yi<yj,因为剩余的右区间的点 y y y值都大于 y j y_j yj,所以此时点 i i i无法与剩下的任何一个右上角的点形成矩形,所以此时要将 i i i踢掉。我们可以用单调栈来维护。因为 y y y值已经从小到大排序,所以在栈中只需让 x x x值单调递减即可。

对于右区间的点 i , j i,j i,j,如果 x i > x j x_i>x_j xi>xj y i < y j y_i<y_j yi<yj,那么 j j j点对 i i i点不会产生任何影响。相反地,如果 x i < x j x_i<x_j xi<xj y i < y j y_i<y_j yi<yj,那点 i i i就不能与 x x x值小于 x j x_j xj 的左区间的点匹配。此时的 j j j i i i有限制作用,需保留。同样地,我们可以使用单调栈来维护,在栈中 x x x值单调递增。

对于每个右区间的点 i i i,设它在栈中前一个位置是点 j j j,那点 i i i的贡献就是左区间的栈中大于 x j x_j xj的点的个数,二分查找即可。总时间复杂度为 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

code

#include<bits/stdc++.h>
using namespace std;
int n,t1,t2,h1[200005],h2[200005];
long long ans=0;
struct node{
	int x,y;
}w[200005];
bool cmp1(node ax,node bx){
	return ax.x<bx.x;
}
bool cmp2(node ax,node bx){
	return ax.y<bx.y;
}
int find(int g){
	int l=1,r=t1,mid;
	while(l<=r){
		mid=l+r>>1;
		if(w[h1[mid]].y<g) 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);
	sort(w+l,w+mid+1,cmp2);
	sort(w+mid+1,w+r+1,cmp2);
	int j=l;t1=t2=0;
	for(int i=mid+1;i<=r;i++){
		while(w[j].y<w[i].y&&j<=mid){
			while(t1&&w[h1[t1]].x<w[j].x) --t1;
			h1[++t1]=j;++j;
		}
		while(t2&&w[h2[t2]].x>w[i].x) --t2;
		h2[++t2]=i;
		if(t2>1) ans+=t1-find(w[h2[t2-1]].y);
		else ans+=t1;
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&w[i].x,&w[i].y);
	}
	sort(w+1,w+n+1,cmp1);
	cdq(1,n);
	printf("%lld",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值