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

[ C q o i 2010 ] 内 部 白 点 [Cqoi2010]内部白点 [Cqoi2010]

Description:

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

Input Format:

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

Output Format:

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

Sample Input:

  • 4
    0 2
    2 0
    -2 0
    0 -2

Sample Output:

  • 5

Tips:

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

TJ:

扫描线+树状数组优化处理,其实不会出现无法停止的情况,并且最多只要1秒就可以把所有可以改变的白点变成黑点。
可以发现由白点转化过来的黑点对结果没有影响,因为转化过来的黑点所在的行列上都已经有其他点了(否则这个黑点是怎么转化过来的),所以只要考虑初始给的黑点就行了。
因为每个点的坐标<=1e9所以首先对横坐标和纵坐标分别进行离散化。因为不存在某个横坐标那整一行都不会有白点变成黑点。
记录每一行的每个点出现的最高位置,必须要这一行的上下边都有点才行。当遇到某一行的某个位置第一次出现这个点的时候,就在树状数组的这个位置上+1,最后一次出现的时候树状数组的这个位置上就-1。
将所有点按一个坐标的大小排序,大小相同时按另一个坐标的大小,然后扫描线按行处理就行了。

CODE

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5+7;
#define All(v) v.begin(),v.end()
struct NODE{
	int x,y;
	bool operator <(const NODE &o) const{
		if(x==o.x) return y<o.y;
		else return x<o.x;
	}
}node[MAXN];
int n;
vector<int> vecx,vecy;
int top[MAXN];
bool appear[MAXN];
int GetID(vector<int> &vec,int x){ return lower_bound(All(vec),x)-vec.begin()+1; }
void preprocess(){
	sort(All(vecx)); sort(All(vecy));
	vecx.erase(unique(All(vecx)),vecx.end());
	vecy.erase(unique(All(vecy)),vecy.end());
	memset(top,255,sizeof(top));
	for(int i=1;i<=n;i++){
		node[i].x = GetID(vecx,node[i].x);
		node[i].y = GetID(vecy,node[i].y);
	}
	sort(node+1,node+1+n);
	for(int i=1;i<=n;i++) top[node[i].y]=max(top[node[i].y],node[i].x);
}
struct Binary_Indexed_Tree{
	int A[MAXN];
	inline int lowbit(int x){ return x&-x; }
	void modify(int pos,int x){
		while(pos<MAXN){
			A[pos]+=x;
			pos+=lowbit(pos);
		}
	}
	int query(int pos){
		int res = 0;
		while(pos){
			res+=A[pos];
			pos-=lowbit(pos);
		}
		return res;
	}
}BIT;
int work(){
	vector<NODE> pool;
	int ans = 0;
	for(int i=1;i<=n;i++){
		if(node[i].x==node[i+1].x) pool.push_back(node[i]);
		else{
			pool.push_back(node[i]);
			int l = pool[0].y;
			int r = pool[pool.size()-1].y;
			ans+=BIT.query(r)-BIT.query(l-1);
			for(int j=0;j<(int)pool.size();j++){
				if(!appear[pool[j].y]) ans++;
				if(top[pool[j].y]!=pool[j].x){
					if(!appear[pool[j].y]) BIT.modify(pool[j].y,1);
					appear[pool[j].y] = 1;
				}
				else{
					if(appear[pool[j].y]) BIT.modify(pool[j].y,-1);
					appear[pool[j].y] = 0;
				}
			}
			pool.clear();
		}
	}
	return ans;
}
void check(){
	#ifndef ONLINE_JUDGE
	freopen("1818/4.out","r",stdin);
	int ans;
	scanf("%d",&ans);
	puts(ans==work()?"True":"False");
	#endif
	return;
}
int main(){
	#ifndef ONLINE_JUDGE
	freopen("1818/4.in","r",stdin);
	#endif
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d %d",&node[i].x,&node[i].y);
		vecx.push_back(node[i].x);
		vecy.push_back(node[i].y);
	}
	preprocess();
	check();
	//printf("%d\n",work());
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值