【BZOJ1818】内部白点

链接:BZOJ1818

解法:树状数组

题意转化为求线段的交点个数。
先将任一坐标离散化,这里以 x x 为例。之后将 x y y 坐标分别排序,求出这些线段。以样例为例,如下图:( x 坐标已离散化,只有在同一横/纵坐标上出现多个点时才会出现线段。)
绿色与蓝色是线段
将线段分两种,横与竖。
将横线段拆为两个点,竖线段不变。然后将这些东西排序。从左至右扫描(若是离散化 y y 坐标则从下至上),扫描到横线段的左端点时,将该横线段的横坐标处的位置 +1 ,扫描到横线段的右端点则 1 − 1 ;扫描到竖线段则查询线段的上下端点,加入答案。区间查询用树状数组维护即可。
细节很多,实现时心态很容易崩( cmp c m p 函数别打错)

代码
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>

using namespace std;

struct data{
    int x,y,i;
    data():x(0),y(0),i(0){}
    data(int a,int b,int j):x(a),y(b),i(j){}
};

struct upd{
    int t,x,l,r;
    upd(){}
    upd(int u,int y,int a,int b):t(u),x(y),l(a),r(b){}
    friend bool operator<(const upd &u1,const upd &u2){if(u1.x==u2.x)return u1.t<u2.t;else return u1.x<u2.x;}
};

int n,rnk[100002],sum[100002];
long long ans;
data dt[100002];
vector<upd> V;

inline bool cmp1(const data &dt1,const data &dt2){if(dt1.x==dt2.x)return dt1.y<dt2.y;return dt1.x<dt2.x;}

inline bool cmp2(const data &dt1,const data &dt2){if(dt1.y==dt2.y)return dt1.x<dt2.x;return dt1.y<dt2.y;}

inline int lowbit(int x){return x&-x;}

void update(int k,int x){for(int i=k;i<=n;i+=lowbit(i))sum[i]+=x;}

int query(int k){int res=0;for(int i=k;i>0;i-=lowbit(i))res+=sum[i];return res;}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d%d",&dt[i].x,&dt[i].y),dt[i].i=i;
    sort(dt+1,dt+n+1,cmp1);
    dt[0].x=2147483647;for(int i=1,j=0;i<=n;++i)if(dt[i].x==dt[i-1].x)rnk[dt[i].i]=j;else rnk[dt[i].i]=++j;
    sort(dt+1,dt+n+1,cmp2);
    for(int i=1;i<=n;++i){int j=i;if(dt[j+1].y==dt[j].y)++j;if(i!=j)V.push_back(upd(1,dt[i].y,rnk[dt[i].i],rnk[dt[j].i]));}
    sort(dt+1,dt+n+1,cmp1);
    for(int i=1;i<=n;++i){int j=i;if(rnk[dt[j+1].i]==rnk[dt[j].i])++j;if(i!=j)V.push_back(upd(2,dt[i].y,rnk[dt[i].i],1)),V.push_back(upd(2,dt[j].y,rnk[dt[j].i],-1));}
    sort(V.begin(),V.end());
    for(upd u:V)if(u.t==2)update(u.l,u.r);else ans+=query(u.r-1)-query(u.l);
    printf("%lld",ans+n);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值