bzoj 1818: [Cqoi2010]内部白点

Posted qt666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 1818: [Cqoi2010]内部白点相关的知识,希望对你有一定的参考价值。

Description

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

Input

输入第一行包含一个整数n,即初始黑点个数。以下n行每行包含两个整数(x,y),即一个黑点的坐标。没有两个黑点的坐标相同,坐标的绝对值均不超过109。

Output

输出仅一行,包含黑点的最终数目。如果变色过程永不终止,输出-1。

Sample Input

4
0 2
2 0
-2 0
0 -2

Sample Output

5

数据范围
36%的数据满足:n < = 500
64%的数据满足:n < = 30000
100%的数据满足:n < = 100000

HINT

Source

题目看起来吓人且鬼畜,但是其实答案过了1s之后就不会变了;

这个可以画一下图,如果某个方向缺了一个点,导致他不能变黑的话,那么这个方向上也不可能有点变黑了,从而他也不可能变黑,所以在1s之后答案确定;

那么接下的问题就是一个扫描线的经典问题了,由于我们在计数的时候不能把原来的黑点也算进去,所以我们取横竖线段的时候要两两断开;

扫描线的具体做法是,通过sort求出所有横线和竖线,然后把竖线拆为两条,从下往上扫描,

遇到竖线的下端点就给该竖线的横坐标+1,遇到上端点就-1,然后遇到横线就查询一下横坐标范围内的权值和;

通过树状数组实现单点修改和区间查询,注意在纵坐标相同时,优先级为撤销>查询>添加;

//MADE BY QT666
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=500050;
int n,hsh[N],tot,tmp;
struct data{
    int x,y;
}g[N];
bool cmp1(const data &a,const data &b){
    if(a.x==b.x) return a.y<b.y;
    else return a.x<b.x;
}
bool cmp2(const data &a,const data &b){
    if(a.y==b.y) return a.x<b.x;
    else return a.y<b.y;
}
struct date{
    int l,r,x,y,op;
}q[N];
int find(int x){return lower_bound(hsh+1,hsh+1+tot,x)-hsh;}
bool cmp3(const date &a,const date &b){
    if(a.y==b.y) return a.op<b.op;
    else return a.y<b.y;
}
int tr[N];
int lowbit(int x){return x&-x;}
void update(int x,int v){
    for(int i=x;i<=tot;i+=lowbit(i)) tr[i]+=v;
}
int query(int x){
    int ret=0;if(x==0) return 0;
    for(int i=x;i;i-=lowbit(i)) ret+=tr[i];
    return ret;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
	scanf("%d%d",&g[i].x,&g[i].y);
	hsh[++tot]=g[i].x;
    }
    sort(hsh+1,hsh+1+tot);tot=unique(hsh+1,hsh+1+tot)-hsh-1;
    sort(g+1,g+1+n,cmp1);
    for(int i=2;i<=n;i++){
	if(g[i].x==g[i-1].x){
	    tmp++;q[tmp].x=find(g[i-1].x),q[tmp].y=g[i-1].y,q[tmp].op=1;
	    tmp++;q[tmp].x=find(g[i].x),q[tmp].y=g[i].y,q[tmp].op=-1;
	}
    }
    sort(g+1,g+1+n,cmp2);
    for(int i=2;i<=n;i++){
	if(g[i].y==g[i-1].y){
	    tmp++;q[tmp].l=find(g[i-1].x),q[tmp].r=find(g[i].x),q[tmp].y=g[i].y,q[tmp].op=0;
	}
    }
    sort(q+1,q+1+tmp,cmp3);int sum=0;
    for(int i=1;i<=tmp;i++){
	if(q[i].op==1) update(q[i].x,1);
	if(q[i].op==-1) update(q[i].x,-1);
	if(q[i].op==0) sum+=query(q[i].r-1)-query(q[i].l);
    }
    printf("%d\n",sum+n);
    return 0;
}

以上是关于bzoj 1818: [Cqoi2010]内部白点的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ1818[Cqoi2010]内部白点 扫描线+树状数组

bzoj 1818: [Cqoi2010]内部白点

BZOJ 1818: [Cqoi2010]内部白点

bzoj 1818 [CQOI 2010] 内部白点 - 扫描线 - 树状数组

1818: [Cqoi2010]内部白点

1818: [Cqoi2010]内部白点