【CF1194E】Count The Rectangles【类扫描线】【单调性】【树状数组】

传送门

题意:给定 N N N条与坐标轴平行的线段,保证不垂直的线段没有交点,求一共构成多少个矩形(以线段交点为顶点)。

1 ≤ N ≤ 5000 1\leq N\leq5000 1N5000

显然是个数据结构乱搞题。

直觉告诉我们先枚举一条线段。

假如我们枚举矩形的上边界,我们希望找到可以构成矩形的其他边。

如果我们找下边界,那左右边界即要穿过上下边界,还要在上下边界的交集内,很难维护。

所以我们可以找左右边界。

我们发现和上边界相交的竖直线都可以当左右边界,所以先找一遍存起来。

这样我们只需要计算下边界和多少个竖直线相交, n ( n − 1 ) / 2 n(n-1)/2 n(n1)/2即可

但是我们还需要满足下边界在竖直线下端点的上面

然后我们发现这个可以用单调性搞掉

即开始时水平线按高度排序,把竖直线丢进去后按下端点的高度排序,枚举下面的水平线作为下边界,如果在竖直线下端点的下面就丢掉,然后区间查询竖直线的个数。

在这里插入图片描述
树状数组维护即可。

复杂度 O ( n 2 l o g n ) O(n^2logn) O(n2logn)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#define MAXN 5005
using namespace std;
typedef long long ll;
struct line{int p,l,r;}hor[MAXN],ver[MAXN],pos[MAXN];
int cnt1,cnt2;
const int N=10005;
struct BIT
{
	int s[MAXN<<1];
	inline int lowbit(const int& x){return x&-x;}
	inline void modify(int x,const int& v){x+=MAXN;for (;x<=N;s[x]+=v,x+=lowbit(x));}
	inline int query(int x){int ans=0;x+=MAXN;for (;x;ans+=s[x],x-=lowbit(x));return ans;};
}bit;
inline bool cmp1(const line& a,const line& b){return a.p<b.p;}
inline bool cmp2(const line& a,const line& b){return a.r<b.r;}
int main()
{
	int n;
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		int x1,y1,x2,y2;
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		if (x1>x2) swap(x1,x2);
		if (y1>y2) swap(y1,y2);
		if (x1==x2) ver[++cnt2]=(line){x1,y1,y2};
		else hor[++cnt1]=(line){y1,x1,x2};
	}
	sort(hor+1,hor+cnt1+1,cmp1);
	ll ans=0;
	for (int i=1;i<=cnt1;i++)
	{
		int tot=0;
		for (int j=1;j<=cnt2;j++)
			if (ver[j].l<=hor[i].p&&hor[i].p<=ver[j].r&&hor[i].l<=ver[j].p&&ver[j].p<=hor[i].r)
				pos[++tot]=ver[j];
		sort(pos+1,pos+tot+1,cmp2);
		for (int j=1;j<=tot;j++) bit.modify(pos[j].p,1);
		int now=1;
		for (int j=i+1;j<=cnt1;j++)
		{
			while (now<=tot&&pos[now].r<hor[j].p) bit.modify(pos[now].p,-1),++now;
			int t=bit.query(hor[j].r)-bit.query(hor[j].l-1);
			ans+=(ll)t*(t-1)/2;
		}
		while (now<=tot) bit.modify(pos[now].p,-1),++now;
	}
	cout<<ans;
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值