POJ3109_Inner Vertices_扫描线段|坐标离散化|树状数组

Inner Vertices
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 2538 Accepted: 720
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


在一个无限大的棋盘上,放置着 N 个黑子。如果一个空白点上下左右都有黑子,就可以给这个点放一个黑子。求最后棋盘上有多少个黑子。


一、扫描线段

以 y 轴为主轴,对每个 y 值画一条平行于 x 轴的直线,纵坐标相等的黑子都在这一条直线上。用 vector 保存条直线上黑子的横坐标。

对每一条线段,遍历它上面的黑子。在纵向检查是否需要涂黑点,横向上统计这个横坐标上已经有多少个空白点了。

二、坐标离散化

因为坐标的范围非常大,而 N 的范围相对较小,利用 N 进行坐标离散化。因为每一个黑子仅对它所在的行和列有影响,而没有黑子的行和列必定不会被放上黑子。所以,我们只需要关注一开始就放有黑子的行和列就好了。因为 N 的最大值为 1e5 ,如果建二维数组还是太大,所以只能把 x 和 y 分别保存。总之就是把坐标的值转化成“第几种坐标”,处理后依然保存在原来的数组中。

三、树状数组的区间修改区间查询


因为时间给的比较多,虽然算法复杂度是 n方,还是能跑出来的。


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;
typedef long long LL;

int N;
const int maxn = 100000 + 100;
int X[maxn], Y[maxn];			//横纵坐标分开保存 
vector<int> Line[maxn];			//扫描线段 线段上黑子的横坐标 
bool Showed[maxn];				//这个 x 坐标在之前是否已经放过黑子 
LL B0[maxn], B1[maxn];			//区间修改区间查询的树状数组 

//坐标离散化 
int Compress(int *A)
{
	//保存所有的黑子的坐标值 
	vector<int> Temp(N);

	for(int i= 1; i<= N; i++)
		Temp[i-1] = A[i];

	//排序后去掉重复的值 
	//借助了 erase函数 和 unique函数 
	sort(Temp.begin(), Temp.end());
	Temp.erase(unique(Temp.begin(), Temp.end()), Temp.end());

	//把坐标值转化成“第几种坐标”
	//借助了二分搜索 
	for(int i= 1; i<= N; i++)
		A[i] = 1 + lower_bound(Temp.begin(), Temp.end(), A[i]) - Temp.begin();

	//返回坐标值总的“种数” 
	return Temp.size();
}

//区间修改区间查询的树状数组

//单点修改 
void Add(LL *B, int i, LL x)
{
	while(i < maxn)
	{
		B[i] += x;
		i += i & -i;
	}
}

//单个树状数组求前缀和 
LL Sum(LL *B, int i)
{
	LL res = 0;

	while(i)
	{
		res += B[i];
		i -= i & -i;
	}

	return res;
}

//区间修改 
void Add(int fr, int to, int x)
{
	Add(B0, fr, -x*(fr-1));
	Add(B1, fr, x);
	Add(B0, to, x*to);
	Add(B1, to, -x);
}

//整体求前缀和 
LL Sum(int i)
{
	return Sum(B0, i) + i * Sum(B1, i);
}

int main()
{
	scanf("%d", &N);

	for(int i= 1; i<= N; i++)
		scanf("%d %d", X+i, Y+i);

	int w = Compress(X);
	int h = Compress(Y);

	//把每个黑子加入到对应的水平线段上 
	for(int i= 1; i<= N; i++)
		Line[Y[i]].push_back(X[i]);

	LL res = N;

	//枚举所有的 y 
	for(int i= 1; i<= h; i++)
	{
		vector<int> &L = Line[i];
		
		//把每一条线段上的 x 从小到大排好序 
		sort(L.begin(), L.end());

		//枚举线段上的每一个黑子 
		for(vector<int>::iterator i = L.begin(); i != L.end(); i++)
		{
			int x = *i;
			
			//这个 x 已经有了多少空白点 
			LL s = Sum(x) - Sum(x-1);

			//之前有过黑子,写空白点就可以涂黑了 
			if(Showed[x]) res += s;
			else Showed[x] = true;

			//处理完当前黑子,它前面的空白子就都不可能被涂黑了
			//在树状数组中删去这些无效的空白子 
			Add(B0, x, -s);

			//统计这一行里可能被涂黑的空白子 
			if(i+1 != L.end())
			{
				//一个区间加 1 
				if(x+1 < *(i+1)-1) Add(x+1, *(i+1)-1, 1);
				
				//单点加 1 
				else if(x+1 == *(i+1)-1) Add(B0, x+1, 1);
			}
		}
	}

	printf("%lld\n", res);

	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值