题目链接:哆啦A梦传送门
题意:二维平面上有n个点,定义一个奇怪的矩形,它是以 l=<x,x<=r,y>=a,每个这样的矩形包含了一些点,问:有多少个不同的矩形。两个矩形不同当且仅当至少有一个点不同。
题解:
我们先按y从大到小排,x从小到大。这样我们就相当于有一条扫描线,从上往下扫。
每到一条扫描线,我们会增加t个新点,我们以添加的每个新点i为结束的集合。此时我们只需分别计算 [1,i-1]和 [i+1,j-1] (j表示同一条扫描线新点i的下一个新点j)的旧点数量为t1,t2。那么就说明新点i左边有t1+1条竖直线,右边有t2+1条竖直线,此时方案数就为(t1+1)*(t2+1)。
依次类推整条扫描线的其它新点。
例如:我们看下副图,当新点A加进去后,A点左右边分别有1,2个点,也就是分别有2,3条线,显然此时有2*3中集合。
这里以A点时算它右边点时,就没有算上B点,因为之后我们算B点时他会算上A点,我们这样做也是为了去重。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int N=2e5+10;
struct node{
int x,y;
bool operator<(const node &p)const{
return y==p.y?x<p.x:y>p.y;
}
}num[N];
int X[N];
bool vis[N];
int tree[N];
int up;
inline int sum(int x)
{
int res=0;
while(x){
res+=tree[x];
x-=x&-x;
}
return res;
}
inline void update(int x)
{
while(x<=up){
tree[x]+=1;
x+=x&-x;
}
}
inline q_sum(int x,int y)
{
return sum(y)-sum(x-1);
}
int vx[N];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&num[i].x,&num[i].y);
X[i]=num[i].x;
}
sort(X+1,X+1+n);
int cnt=unique(X+1,X+1+n)-(X+1);
///离散化x值
for(int i=1;i<=n;i++){
num[i].x=lower_bound(X+1,X+1+cnt,num[i].x)-X;
}
sort(num+1,num+1+n);
LL ans=0;
up=cnt+1;
///now当前扫描线的第一个点,next当前扫描线的其他点
int now=1,next=1;
while(now<=n)
{
int tot=0;
while(num[now].y==num[next].y){
if(!vis[num[next].x]) ///已经加过了,就不用再加了
update(num[next].x);
vis[num[next].x]=1;
vx[++tot]=num[next].x;
next++;
}
vx[++tot]=cnt+1;
for(int i=1;i<=tot-1;i++)
{
///计算以当前新点为结束的左,右分别有多少个旧点
int t1=q_sum(1,vx[i]-1);
int t2=q_sum(vx[i]+1,vx[i+1]-1);
ans+=1LL*(t1+1)*(t2+1);
}
now=next;
}
printf("%lld\n",ans);
return 0;
}