Luogu5816 [CQOI2010]内部白点

40 篇文章 0 订阅
5 篇文章 0 订阅
本文详细介绍了如何解决一个二维网格中黑点扩散的问题,其中黑点会将相邻的内部白点染黑。通过分析,确定不存在无限扩散的情况,并提出使用线段树的数据结构配合扫描线算法来一次性计算最终的黑点数量。文章提供了具体的实现思路,包括处理特殊情况、离散化坐标、维护线段树等关键步骤,以及完整的C++代码示例,展示了如何高效地求解此类问题。
摘要由CSDN通过智能技术生成

原题链接:https://www.luogu.com.cn/problem/P5816

内部白点

题目描述

无限大正方形网格里有 n n n 个黑色的顶点,所有其他顶点都是白色的(网格的顶点即坐标为整数的点,又称整点)。每秒钟,所有内部白点同时变黑,直到不存在内部白点为止。你的任务是统计最后网格中的黑点个数。

内部白点的定义:一个白色的整点 P ( x , y ) P(x,y) P(x,y) 是内部白点当且仅当 P P P 在水平线的左边和右边各至少有一个黑点(即存在 x 1 < x < x 2 x_1 < x < x_2 x1<x<x2 使得 ( x 1 , y ) (x_1,y) (x1,y) ( x 2 , y ) (x_2,y) (x2,y) 都是黑点),且在竖直线的上边和下边各至少有一个黑点(即存在 y 1 < y < y 2 y_1 < y < y_2 y1<y<y2 使得 ( x , y 1 ) (x,y_1) (x,y1) ( x , y 2 ) (x,y_2) (x,y2) 都是黑点)。

输入格式

输入第一行包含一个整数 n n n,即初始黑点个数。

以下 n n n 行每行包含两个整数 x x x y y y,即一个黑点的坐标。没有两个黑点的坐标相同,坐标的绝对值均不超过 1 0 9 10^9 109

输出格式

输出仅一行,包含黑点的最终数目。

如果变色过程永不终止,输出-1。

输入输出样例

输入 #1
4
0 2
2 0
-2 0
0 -2
输出 #1
5

说明/提示

数据范围

对于 36 % 36\% 36% 的数据, n ≤ 500 n \le 500 n500

对于 64 % 64\% 64% 的数据, n ≤ 3 × 1 0 4 n \le 3 \times 10^4 n3×104

对于 100 % 100\% 100% 的数据, n ≤ 1 0 5 n \le 10^5 n105

题解

思路比较简单,但是细节挺多。

首先,必定不会有 − 1 -1 1的情况出现,因为一个点从白点变成黑点当且仅当这个白点上下左右都有黑点,而这个白点变成黑点后,它的作用完全可以被催生它的四个黑点取代,即如果新的黑点可以让其他白点变黑那么原来的黑点一样可以。

解决了这个问题,我们就不需要再纠结新产生的黑点对其他白点的影响了,利用初始条件给定的黑点我们就能一次得出答案。

下面考虑怎么一次求出答案,白点变黑点的条件是上下左右均有黑点,我们同样可以套用扫描线的思路,数据结构维护一维,延伸推进一维。在这道题中,可以用线段树来维护横向的信息,当线段树上某个位置有值的时候,就说明此时这一列上下均有一个黑点,再找到当前纵坐标下最左/右端的两个黑点,求这两个黑点之前线段树区间和就是新增的黑点数量。

具体做法是,从下往上地遍历所有点,如果当前点是某个横坐标下最下方的点,那么就在线段树上给该位置 + 1 +1 +1,如果当前点是某个横坐标下最上方的点,那么就在线段树上给该位置 − 1 -1 1,这样就保证线段树上有值的位置就是上下都有黑点的位置。

一些细节:横坐标需要离散化、注意独占一纵列的黑点、注意本来上下左右就有黑点的黑点、注意左右本来就有黑点且本身又是纵线最下面的点的黑点。

代码

《取max的初始值设置成0被负数卡了一天且对拍到自闭的故事》

#include<bits/stdc++.h>
#define ls v<<1
#define rs v<<1|1
using namespace std;
const int M=1e5+5;
struct Point{int x,y,val;}pt[M];
struct node{int le,ri,sum;}tree[M<<2];
bool cmpy(Point a,Point b){return a.y==b.y?a.x<b.x:a.y<b.y;}
bool cmpx(Point a,Point b){return a.x==b.x?a.y<b.y:a.x<b.x;}
int n,ans,x[M],tot;
void up(int v){tree[v].sum=tree[ls].sum+tree[rs].sum;}
void build(int v,int le,int ri)
{
    tree[v].le=x[le],tree[v].ri=x[ri];
    if(le==ri)return;
    int mid=le+ri>>1;
    build(ls,le,mid),build(rs,mid+1,ri);
}
void add(int v,int x,int val)
{
    if(val>1)return;
    if(tree[v].le==tree[v].ri){tree[v].sum+=val;return;}
    if(x<=tree[ls].ri)add(ls,x,val);
    else add(rs,x,val);
    up(v);
}
int ask(int v,int le,int ri)
{
    if(le<=tree[v].le&&tree[v].ri<=ri)return tree[v].sum;
    int r=0;
    if(le<=tree[ls].ri)r=ask(ls,le,ri);
    if(tree[rs].le<=ri)r+=ask(rs,le,ri);
    return r;
}
void in()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d%d",&pt[i].x,&pt[i].y),x[i]=pt[i].x;
}
void ac()
{
    sort(pt+1,pt+1+n,cmpx);
    for(int i=1;i<=n;++i)
    {
        x[i]=pt[i].x;
        if(pt[i].x!=pt[i-1].x&&pt[i].x!=pt[i+1].x){pt[i].val=2;continue;}
        if(pt[i].x!=pt[i-1].x)pt[i].val=1;
        else if(pt[i].x!=pt[i+1].x)pt[i].val=-1;
    }
    tot=unique(x+1,x+1+n)-x-1;
    build(1,1,tot);
    sort(pt+1,pt+1+n,cmpy);
    for(int i=1,j=1,le,ri;i<=n;++i)
    {
        for(j=i,le=INT_MAX,ri=-INT_MAX;pt[i].y==pt[i+1].y;++i)add(1,pt[i].x,pt[i].val),le=min(le,pt[i].x),ri=max(ri,pt[i].x);
        add(1,pt[i].x,pt[i].val),le=min(le,pt[i].x),ri=max(ri,pt[i].x);
        for(j+=1;j<i;++j)ans-=(!pt[j].val||pt[j].val==1);
        if(le!=ri)ans+=ask(1,le+1,ri-1);
    }
    printf("%d\n",ans+n);
}
int main()
{
    in(),ac();
    //system("pause");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值