茶颜悦色 【CCPC-Wannafly & Comet OJ 夏季欢乐赛(2019)】【线段树+扫描线】

题目链接

题目描述
茶颜悦色也太好喝了!鸡尾酒在长沙的各种茶颜悦色的店铺中流连忘返。他发现长沙有炒鸡多的茶颜悦色店,走两步就能遇到一家。
“方圆一公里能有十家茶颜悦色!”鸡尾酒感叹了起来。
于是他想到了一个问题:最密集的地方,能有多少家茶颜悦色的店?
鸡尾酒将长沙地图用一个二维平面表示,他统计出了每个茶颜悦色店铺的坐标。
他想知道,在一个边长为 kk 且底边平行于 xx 轴的正方形中,最多有多少家茶颜悦色。
若茶颜悦色恰好在正方形的边上,也算在正方形之中。

输入描述
输入第一行包含两个正整数 n,n, (n \le10{5},k≤10{9}n≤10 5 ,k≤10 9) 代表茶颜悦色店的数量和正方形的边长。
接下来 nn 行每行有两个整数,描述一家茶颜悦色店的坐标 x_{i},y_{i}x i ,y i(0 \le x_{i}, y_{i} \le 10^{9}0≤x i ,y i ≤10 9) 保证不会出现重复的坐标。
输出描述
输出一行一个正整数表示答案。

解题思路

贴官方题解:
由题设 k 为正方形边长。首先我们可以通过确定正方形的底边来
确定正方形。比如底边是由[x, y],[x + k, y]两点连成的边,那么这样的
底边可以确定一个由[x, y],[x + k, y],[x, y + k],[x + k, y + k]四个点组成的正方形。假设我
们用左下角的点[x, y]来代表这个正方形(下同)。

现在我们来考虑某个点(a,b)可以被怎样的正方形包含。那么我们
会发现,左下角的点的 x 坐标满足(a − k ≤ x ≤ a)且 y 坐标满足
(b − k ≤ y ≤ b)会包含(a,b)这个点。

我们不妨用扫描线的思想来解决这道题。我们从左到右地扫描 x
坐标,并用线段树维护当前 y 坐标上的点数量。即当目前扫描到区间
[a − k, a]时,点(a,b)对 y 坐标为[b-k,b]中的正方形有贡献。所以当
扫描到 a-k 时,将区间[b-k,b]全部+1。当扫描到 a 时,将区间对应
地-1。这样就可以描述(a,b)点对题目答案的贡献了。

具体的做法:每个点(a,b)存两遍,一遍存(a-k,b)且置 flag 为 1,
代表扫描到此点时要将区间+1,第二遍存(a,b)且置 flag 为-1,代表
扫描到此点时要将区间-1。然后按 x 坐标排序,用线段树扫描 x 坐标
即可。每次+1 之后区间查询全局最大值,更新答案。
时间复杂度O(?log2 ?)

注意:其中要用离散化,记录边的时候,左边的点flag记1,右边的记-1,这个地方wa惨我了。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

const int N=1e5+5;
struct linee
{
    long long x,y,h;
    int flag;
}a[N*4];
long long yy[N*4];
struct node
{
    int l,r,lazy,sum;
};
node e[N*10];

int cmp(linee x,linee y)
{
    if(x.x==y.x)
        return x.flag>y.flag;
    return x.x<y.x;
}
void build(int root,int l,int r)
{
    e[root].l=l;
    e[root].r=r;
    e[root].lazy=e[root].sum=0;
    if(e[root].l==e[root].r)
        return ;
    else
    {
        build(root*2,l,(l+r)/2);
        build(root*2+1,(l+r)/2+1,r);
    }
}
void updown(int root)
{
    if(e[root].lazy)
    {
        e[root*2].lazy+=e[root].lazy;
        e[root*2+1].lazy+=e[root].lazy;
        e[root*2].sum+=e[root].lazy;
        e[root*2+1].sum+=e[root].lazy;
        e[root].lazy=0;
    }
}
void update(int root,int l,int r,int v)
{
    if(e[root].l>=l&&e[root].r<=r)
    {
        e[root].sum+=v;
        e[root].lazy+=v;
        return ;
    }
    updown(root);
    int mid=(e[root].l+e[root].r)/2;
    if(r<=mid)
        update(root*2,l,r,v);
    else
    {
        if(l>mid)
            update(root*2+1,l,r,v);
        else
        {
            update(root*2,l,mid,v);
            update(root*2+1,mid+1,r,v);
        }
    }
    e[root].sum=max(e[root*2].sum,e[root*2+1].sum);
}
int main()
{
    int n;
    long long k;
    scanf("%d %lld",&n,&k);
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld %lld",&a[i].x,&a[i].y);
        a[i].flag=-1;
        a[i+n].x=a[i].x-k;a[i+n].y=a[i].y;
        a[i+n].flag=1;
        yy[cnt++]=a[i].y;
        yy[cnt++]=a[i].y-k;
    }
    sort(a+1,a+1+2*n,cmp);
    sort(yy,yy+cnt);
    int m=unique(yy,yy+cnt)-yy;
    build(1,0,m);
    int ans=0;
    for(int i=1;i<=2*n;i++)
    {
        int l=lower_bound(yy,yy+cnt,(a[i].y-k))-yy;
        int r=lower_bound(yy,yy+cnt,a[i].y)-yy;
        //printf("%d %d %d\n",l,r,a[i].flag);
        update(1,l,r,a[i].flag);
        if(a[i].flag==1)
        {
            ans=max(ans,e[1].sum);
        }
    }
    printf("%d\n",ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值