题意:给定 N N N条与坐标轴平行的线段,保证不垂直的线段没有交点,求一共构成多少个矩形(以线段交点为顶点)。
1 ≤ N ≤ 5000 1\leq N\leq5000 1≤N≤5000
显然是个数据结构乱搞题。
直觉告诉我们先枚举一条线段。
假如我们枚举矩形的上边界,我们希望找到可以构成矩形的其他边。
如果我们找下边界,那左右边界即要穿过上下边界,还要在上下边界的交集内,很难维护。
所以我们可以找左右边界。
我们发现和上边界相交的竖直线都可以当左右边界,所以先找一遍存起来。
这样我们只需要计算下边界和多少个竖直线相交, n ( n − 1 ) / 2 n(n-1)/2 n(n−1)/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;
}