POJ3109-Inner Vertices-离散化+扫描线

Inner Vertices
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 2422 Accepted: 666
Case Time Limit: 2000MS
Description

There is an infinite square grid. Some vertices of the grid are black and other vertices are white.

A vertex V is called inner if it is both vertical-inner and horizontal-inner. A vertex V is called horizontal-inner if there are two such black vertices in the same row that V is located between them. A vertex V is called vertical-inner if there are two such black vertices in the same column that V is located between them.

On each step all white inner vertices became black while the other vertices preserve their colors. The process stops when all the inner vertices are black.

Write a program that calculates a number of black vertices after the process stops.

Input

The first line of the input file contains one integer number n (0 ≤ n ≤ 100 000) — number of black vertices at the beginning.

The following n lines contain two integer numbers each — the coordinates of different black vertices. The coordinates do not exceed 109 by their absolute values.

Output

Output the number of black vertices when the process stops. If the process does not stop, output -1.

Sample Input

4
0 2
2 0
-2 0
0 -2
Sample Output

5
Hint
这里写图片描述
题意:无限大棋盘,给定一些黑棋的位置,同时如果一个白棋上下左右都有黑棋这个白棋就会变成黑棋,求最终黑棋的个数,如果变化不会停止就返回-1.
思路:实际上这个变化一定会停止,简单思考即可以知道。问题是会不会引起连锁反应?其实不会,比如看下图
这里写图片描述
如果由1235点生成了4点,那么4567点又可以生成8点,但是其实有2点的时候就可以生成8点。所以如果生成的点可以生成新的点的前提其实在这个点生成之前就已经确定了。所以不存在只有新生成的某一个点才能生成另一个新点的说法。所以这个时候我们可以利用扫描线的思想。但是由于坐标巨大,所以我们必须对坐标进行离散化处理,然后扫描线中得线段树的数组才开的出来。关于扫描线的知识请移步http://www.cnblogs.com/scau20110726/archive/2013/04/12/3016765.html,所以我们可以将每一个竖直方向的线段的下端点赋值1,上端点赋值-1,然后按照他们的y坐标排序,然后按照图上所有黑棋的y坐标排序后在相同的y坐标中求两个相邻x坐标之间区域内有多少个线段的下端,那么对应的x坐标的这个y坐标就可以生成一个黑棋。
给出一组我自己的测试数据

11
2 0
1 1
4 1
3 2
0 3
3 3
5 3
2 4
10000 4
5 5
4 1000000000
输出16
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 100010 * 2;
typedef long long ll;
typedef struct node//黑棋的位置
{
	int x,y;
}node;
node a[maxn];
typedef struct line//竖直线段的前后端
{
	int y,x,value;
}line;
vector<line> que;

//按照x从小到大,相同的x按照y值从小到大排序
bool cmpx(node x,node y){
	if(x.x<y.x) return true;
	else if(x.x>y.x) return false;
	else{
		if(x.y<y.y) return true;
		else return false;
	}
}
bool tcmp(line x,line y){
	return x.y < y.y;
}
//按照y值从小到大,相同的y按照x值从小到大排序
bool cmpy(node x,node y){
	if(x.y<y.y) return true;
	else if(x.y>y.y) return false;
	else{
		if(x.x<y.x) return true;
		else return false;
	}
}
typedef struct addtree
{
	//区间用[ql,qr],[l,r]表示
	int t[maxn*2];//现在由于坐标还没有离散化,所以我们需要对坐标进行离散化,否则这个树是建立不出来的
	//向上更新区间和函数
	//比如区间[1,8]我们要在5位置将原来的数加上2就需要传入(5,2,1,1,8)
	void update(int loc,int x,int k,int l,int r){
		if(l==r) t[k]+=x;
		else{
			int mid = l + (r-l)/2;
			if(loc<=mid) update(loc,x,k*2,l,mid);
			else update(loc,x,k*2+1,mid+1,r);
			t[k] = t[k*2] + t[k*2+1];
		}
	}
	//查询区间和
	//比如区间[1,8]我们要查询[3,6]区间的区间和就要传入(3,6,1,1,8)
	int query(int ql,int qr,int k,int l,int r){
		if(ql<=l && r<=qr) return t[k];
		int mid = l + (r-l)/2,ans=0;
		if(ql<=mid) ans+=query(ql,qr,k*2,l,mid);
		if(mid+1<=qr) ans+=query(ql,qr,k*2+1,mid+1,r);
		return ans;
	}

}addtree;//单点加上一个值求区间和的线段树
addtree at;
int main(){
	int n;
	cin >> n;
	for(int i=0;i<n;i++) scanf("%d%d",&a[i].x,&a[i].y);

	//对x和y的坐标进行离散化
	int maxx=1,maxy=1,nowloc=0,lastloc=0;
	sort(a,a+n,cmpx);//按照x从小到大到y从小到大排序
	while(lastloc < n){
		nowloc = lastloc;
		while(lastloc < n && a[lastloc].x==a[nowloc].x) lastloc++;
		for(int i=nowloc;i<lastloc;i++) a[i].x=maxx;
		maxx++;
	}
	nowloc=0;lastloc=0;
	sort(a,a+n,cmpy);
	while(lastloc < n){
		nowloc = lastloc;
		while(lastloc < n && a[lastloc].y==a[nowloc].y) lastloc++;
		for(int i=nowloc;i<lastloc;i++) a[i].y=maxy;
		maxy++;
	}
	//离散化结束


	sort(a,a+n,cmpx);//再次按照x坐标进行排序
	//for(int i=0;i<n;i++) cout << a[i].x << " " << a[i].y << endl;
	for(int i=0;i<n-1;i++){
		if(a[i].x==a[i+1].x){
			que.push_back( (line){a[i].y,a[i].x,1} );
			que.push_back( (line){a[i+1].y,a[i+1].x,-1} );
		}
	}
	sort(que.begin(),que.end(),tcmp);//这里存储的都是线段的两个端点,但是还有非线段的点
	sort(a,a+n,cmpy);//对所有的点进行按照y的排序
	int cnt=0,res=n,len=que.size();
	memset(at.t,0,sizeof(at.t));
	for(int i=0;i<n-1;i++){
		int nowy=a[i].y;
		//先处理线段的端点
		while(cnt < len && que[cnt].y<=nowy){
			at.update(que[cnt].x,que[cnt].value,1,1,maxn);
			cnt++;
		}
		//再处理一些区间的问题
		if(a[i+1].y==a[i].y && a[i+1].x - a[i].x >1){
			res += at.query(a[i].x+1,a[i+1].x-1,1,1,maxn);
		}
	}
	cout << res << endl;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

门豪杰

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

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

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

打赏作者

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

抵扣说明:

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

余额充值