大意: 给定$n$个平面点, 定义集合$S(l,r,a)$表示横坐标$[l,r]$纵坐标$[a,infty]$内的所有点. 求可以得到多少种不同的集合.
从上往下枚举底层最右侧点, 树状数组统计贡献.
#include <iostream> #include <algorithm> #include <cstdio> #define REP(i,a,n) for(int i=a;i<=n;++i) using namespace std; typedef long long ll; const int N = 1e6+10, INF = 0x3f3f3f3f; int n, c[N], vis[N], b[N]; struct _ { int x,y; bool operator < (const _&rhs) const { if (y!=rhs.y) return y>rhs.y; return x<rhs.x; } } a[N]; int ID(int x) { return lower_bound(b+1,b+1+*b,x)-b; } int qry(int x) { int r = 0; for (; x; x^=x&-x) r+=c[x]; return r; } int qry(int l, int r) { return qry(ID(r))-qry(ID(l-1)); } void add(int x) { x = ID(x); if (!vis[x]) { vis[x] = 1; for (; x<=*b; x+=x&-x) ++c[x]; } } int main() { scanf("%d", &n); REP(i,1,n) { scanf("%d%d",&a[i].x,&a[i].y); b[++*b]=a[i].x; b[++*b]=a[i].x-1; } b[++*b]=0,b[++*b]=INF; sort(b+1,b+1+*b),*b=unique(b+1,b+1+*b)-b-1; sort(a+1,a+1+n); ll ans = 0; REP(i,1,n) { int j=i; while (j<n&&a[j+1].y==a[i].y) ++j; REP(k,i,j) { int L = qry(1,a[k].x-1); int R = qry(a[k].x+1,k==j?INF:a[k+1].x-1); ans += (ll)(L+1)*(R+1); add(a[k].x); } i = j; } printf("%lld ", ans); }