传送门

注意到矩形往上是无限的,考虑把点按 $y$ 从大到小考虑

对于枚举到高度为 $h$ 的点,设当前高度大于等于 $h$ 的点的所有点的不同的 $x$ 坐标数量为 $cnt$

那么对于这一层高度 $h$ 我们就有 $cnt(cnt+1)/2$ 种不同的 $l$,$r$ ,使得矩形内点集不同

发现对于某些 $x$ 在这一层相邻两点之间,高度大于 $h$ 的点,这样又重复算了它们的贡献,所有要再扣掉

直接用树状数组维护一下当前区间内不同的 $x$ 的数量即可

因为离散化了判断 $x$ 是否出现过显然直接开一个桶就行....

代码是真的挺简单的,也很容易看懂吧...

#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=4e5+7;
int n,b[N],m;
struct poi {
    int x,y;
    poi (int _x=0,int _y=0) { x=_x,y=_y; }
    inline bool operator < (const poi &tmp) const {
        return y!=tmp.y ? y>tmp.y : x<tmp.x;//按y从大到小,x从小到大排序
    }
}p[N];
struct BIT {
    int t[N];
    inline void add(int x) { while(x<=m) t[x]++,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 tmp[N],tot;
bool vis[N];
int main()
{
    n=m=read(); int x,y;
    for(int i=1;i<=n;i++)
    {
        x=read(),y=read();
        p[i]=poi(x,y); b[i]=x;
    }
    sort(b+1,b+m+1); m=unique(b+1,b+m+1)-b-1;
    for(int i=1;i<=n;i++) p[i].x=lower_bound(b+1,b+m+1,p[i].x)-b;//离散化
    sort(p+1,p+n+1); int cnt=0;
    for(int i=1;i<=n;i++)
    {
        int r=i; tmp[tot=1]=0;/*注意加入0*/ tmp[++tot]=p[i].x;
        while(p[r+1].y==p[i].y) tmp[++tot]=p[++r].x;
        tmp[++tot]=m+1;//注意加入m+1
        tot=unique(tmp+1,tmp+tot+1)-tmp-1;
        for(int j=1;j<tot;j++)
        {
            int L=tmp[j]+1,R=tmp[j+1]-1;
            int t=T.ask(R)-T.ask(L-1);
            Ans-=1ll*t*(t+1)/2;//扣掉会重复算的方案数
        }
        for(int j=i;j<=r;j++)
        {
            if(vis[p[j].x]) continue;
            vis[p[j].x]=1; T.add(p[j].x); cnt++;//不同就加入树状数组
        }
        Ans+=1ll*cnt*(cnt+1)/2; i=r;
    }
    printf("%lld\n",Ans);
    return 0;
}
01-21 07:04