传送门

看到 $n<=5000$,直接暴力枚举左右两条竖线

然后考虑怎么计算高度在某个范围内,左端点小于等于某个值,右端点大于等于某个值的横线数量

直接用权值树状数组维护当前高度在某个区间内的横线数量

考虑先固定左边的竖线,然后枚举从左到右枚举右边的竖线,那么随着右边竖线的右移,合法的横线(右端点大于等于某个值的横线)数量只会越来越少

所以枚举右边竖线的时候同时动态维护一个指向当前最左的右端点合法的横线,然后动态维护树状数组就行了

答案也很容易计算,在固定了左右竖线的情况下,设中间有 $p$ 个最小的不可分割的矩形,那么贡献即为 $p(p+1)/2$

(枚举大矩形包含了 $k$ 小矩形,那么就是 $\sum_{k=1}^{p}(p-k+1)$)

然后因为坐标系有负数所以统一加上 $5001$ 即可

复杂度 $n^2 \log 10000$,很稳

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=10007;
struct Line {//竖线
    int x,l,r;
    Line (int _x=0,int _l=0,int _r=0) { x=_x,l=_l,r=_r; }
    inline bool operator < (const Line &tmp) const {
        return x!=tmp.x ? x<tmp.x : l<tmp.l;
    }
}D[N];
struct Line2 {//横线
    int h,l,r;
    Line2 (int _h=0,int _l=0,int _r=0) { h=_h,l=_l,r=_r; }
}H1[N],H2[N];
inline bool cmp1(Line2 &A,Line2 &B) { return A.l!=B.l ? A.l<B.l : A.r<B.r; }//把横线按左端点排序
inline bool cmp2(Line2 &A,Line2 &B) { return A.r!=B.r ? A.r<B.r : A.l<B.l; }//按右端点排序
int n,m,md,mh;
struct BIT {//树状数组
    int t[N];
    inline void init() { memset(t,0,sizeof(t)); }
    inline void add(int x,int v) { while(x<=m) t[x]+=v,x+=x&-x; }
    inline int ask(int x) { int res=0; while(x) res+=t[x],x-=x&-x; return res; }
}T;
ll Ans;
int main()
{
    n=read(); int a,b,c,d;
    for(int i=1;i<=n;i++)
    {
        a=read()+5001,b=read()+5001,c=read()+5001,d=read()+5001;
        m=max(m,max(b,d));
        if(a==c)
        {
            if(b>d) swap(b,d);
            D[++md]=Line(a,b,d);
        }
        else
        {
            if(a>c) swap(a,c);
            H1[++mh]=Line2(b,a,c); H2[mh]=H1[mh];
        }
    }
    sort(D+1,D+md+1); sort(H1+1,H1+mh+1,cmp1); sort(H2+1,H2+mh+1,cmp2);
    for(int i=1;i<=md;i++)//枚举左边的竖线
    {
        int l=1,r=1;//动态维护指针
        while(l<=mh&&H1[l].l<=D[i].x) T.add(H1[l].h,1),l++;
        while(r<=mh&&H2[r].r<D[i].x) T.add(H2[r].h,-1),r++;
        for(int j=i+1;j<=md;j++)
        {
            while(r<=mh&&H2[r].r<D[j].x)
            {
                if(H2[r].l<=D[i].x) T.add(H2[r].h,-1);//注意要判断是之前加入过的横线
                r++;
            }
            if(D[i].x==D[j].x) continue;//相等不合法
            int L=max(D[i].l,D[j].l),R=min(D[i].r,D[j].r);
            if(L>=R) continue;//相等不合法
            int p=T.ask(R)-T.ask(L-1)-1; if(p<=0) continue;//构不成矩形
            Ans+=1ll*p*(p+1)/2;//累计贡献
        }
        while(r<=mh)//记得清空树状数组
        {
            if(H2[r].l<=D[i].x) T.add(H2[r].h,-1);
            r++;
        }
    }
    printf("%lld\n",Ans);
    return 0;
}
01-26 22:59