传送门
题意:给定NNN条与坐标轴平行的线段,保证不垂直的线段没有交点,求一共构成多少个矩形(以线段交点为顶点)。
1≤N≤50001\leq N\leq50001≤N≤5000
显然是个数据结构乱搞题。
直觉告诉我们先枚举一条线段。
假如我们枚举矩形的上边界,我们希望找到可以构成矩形的其他边。
如果我们找下边界,那左右边界即要穿过上下边界,还要在上下边界的交集内,很难维护。
所以我们可以找左右边界。
我们发现和上边界相交的竖直线都可以当左右边界,所以先找一遍存起来。
这样我们只需要计算下边界和多少个竖直线相交,n(n−1)/2n(n-1)/2n(n−1)/2即可
但是我们还需要满足下边界在竖直线下端点的上面
然后我们发现这个可以用单调性搞掉
即开始时水平线按高度排序,把竖直线丢进去后按下端点的高度排序,枚举下面的水平线作为下边界,如果在竖直线下端点的下面就丢掉,然后区间查询竖直线的个数。
树状数组维护即可。
复杂度O(n2logn)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;
}