[BZOJ1818][Cqoi2010]内部白点(扫描线+线段树)

211 篇文章 0 订阅
70 篇文章 0 订阅

题目描述

传送门

题目大意:无限大正方形网格里有n个黑色的顶点,所有其他顶点都是白色的(网格的顶点即坐标为整数的点,又称整点)。每秒钟,所有内部白点同时变黑,直到不存在内部白点为止。统计最后网格中的黑点个数。 内部白点的定义:一个白色的整点P(x,y)是内部白点当且仅当P在水平线的左边和右边各至少有一个黑点(即存在x1 < x < x2使得(x1,y)和(x2,y)都是黑点),且在竖直线的上边和下边各至少有一个黑点(即存在y1 < y < y2使得(x,y1)和(x,y2)都是黑点)。

题解

显然整个图只会变身一次→_→
所以答案即是将每一行的黑点连起来,每一列的黑点连起来搞出来一坨线段,求有多少个交点
注意线段长度为0(就是一个点)也计算在内
用扫描线的方法,将纵坐标离散,按横坐标排序,然后横向的线段的两个端点分别+1-1,每一次查询纵向的线段的范围内有多少个点就行了,注意对于每一个横坐标,先把能加进去的都加进去,统计完答案后再减去
其实这道题因为没有重复的纵坐标直接搞一个bit单点修改区间查询就行了,我比较sb写了线段树

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 300005

int n,cnt,ans,LSH,lsh[N];
struct P{int x,y;}p[N];
struct OPT{int x,y,l,r,val;}opt[N];
int cover[N<<1],sum[N<<1];

int cmpx(P a,P b)
{
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
int cmpy(P a,P b)
{
    return a.y<b.y||(a.y==b.y&&a.x<b.x);
}
int cmp(OPT a,OPT b)
{
    return a.x<b.x||(a.x==b.x&&a.val>b.val);
}
void update(int now)
{
    sum[now]=sum[now<<1]+sum[now<<1|1];
}
void change(int now,int l,int r,int x,int v)
{
    int mid=(l+r)>>1;
    if (l==r)
    {
        cover[now]+=v;
        if (cover[now]) sum[now]=1;
        else sum[now]=0;
        return;
    }
    if (x<=mid) change(now<<1,l,mid,x,v);
    else change(now<<1|1,mid+1,r,x,v);
    update(now);
}
int query(int now,int l,int r,int lr,int rr)
{
    int mid=(l+r)>>1,ans=0;
    if (lr<=l&&r<=rr) return sum[now];
    if (lr<=mid) ans+=query(now<<1,l,mid,lr,rr);
    if (mid+1<=rr) ans+=query(now<<1|1,mid+1,r,lr,rr);
    return ans;
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;++i)
    {
        scanf("%d%d",&p[i].x,&p[i].y);
        lsh[++LSH]=p[i].y;
    }
    sort(lsh+1,lsh+LSH+1);LSH=unique(lsh+1,lsh+LSH+1)-lsh-1;
    for (int i=1;i<=n;++i)
        p[i].y=lower_bound(lsh+1,lsh+LSH+1,p[i].y)-lsh;
    sort(p+1,p+n+1,cmpy);
    for (int i=1,j;i<=n;i=j)
    {
        j=i;
        while (p[j].y==p[i].y)
            ++j;
        opt[++cnt].x=p[i].x,opt[cnt].y=p[i].y,opt[cnt].val=1;
        opt[++cnt].x=p[j-1].x,opt[cnt].y=p[i].y,opt[cnt].val=-1;
    }
    sort(p+1,p+n+1,cmpx);
    for (int i=1,j;i<=n;i=j)
    {
        j=i;
        while (p[j].x==p[i].x)
            ++j;
        opt[++cnt].x=p[i].x,opt[cnt].l=p[i].y,opt[cnt].r=p[j-1].y;
    }
    sort(opt+1,opt+cnt+1,cmp);
    for (int i=1;i<=cnt;++i)
    {
        if (opt[i].val)
            change(1,1,LSH,opt[i].y,opt[i].val);
        else
            ans+=query(1,1,LSH,opt[i].l,opt[i].r);
    }
    printf("%d\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值