原题链接: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 n≤500。
对于 64 % 64\% 64% 的数据, n ≤ 3 × 1 0 4 n \le 3 \times 10^4 n≤3×104 。
对于 100 % 100\% 100% 的数据, n ≤ 1 0 5 n \le 10^5 n≤105 。
题解
思路比较简单,但是细节挺多。
首先,必定不会有 − 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");
}