POJ 3109 Inner Vertices

POJ 3109 Inner Vertices

题意:黑白棋・改:无限大的棋盘上,在横向和纵向上被包围的白子会变成黑子,求最终黑子个数

 思路:首先要想明白这个改变的过程会一次完成,不会因为新产生的黑子而产生更多黑子。

    扫描线 + 树状数组优化。对于线上的点,只要它左右两边都有黑点那么该点就要被算进去,因此我们可以将y坐标离散化以后,先预处理出每一行最左端和最右端的点,用树状数组代表扫描线上的点,遇到最左端的黑点就将树状数组里对应的位置++,遇到最右端的黑点就将对应的位置--,然后扫描线每前进一次就计算出该位置的上下两个端点,用树状数组求出端点间黑点的个数。这里的思想和imos法是一致的,记录变化量,然后累加。

  哎,写了好久总是WA掉了,找到了网上一个代码,先放在这里,过会儿再debug...

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define MAX_N 100000 + 16
typedef long long LL;
 
LL bit0[MAX_N], bit1[MAX_N];	// 双BIT实现O(logn)区间加法
int N, X[MAX_N], Y[MAX_N];
vector<int> line[MAX_N];	// 每个黑子构成的一条条水平线,y坐标作为下标
bool visited[MAX_N];		// 是否已经算过了
 
 
// 求和sum{a[t], 0 <= t <= i}
LL sum(LL *b, int i)
{
	int s = 0;
	while (i > 0) 
	{
		s += b[i];
		i -= (i & -i);
	}
	return s;
}
 
// 求和a[from, to)
LL sum(LL* bit, int from, int to)
{
	return sum(bit, to - 1) - sum(bit, from - 1);
}
 
// 执行 a[i] += v
void add(LL *b, int i, LL v)
{
	while (i <= MAX_N)
	{
		b[i] += v;
		i += (i & -i);
	}
}
// 基础bit结束
 
// 双BIT
 
//[0,n]
LL sum(int i)
{
	return sum(bit1, i) * i + sum(bit0, i);
}
 
//(from, to]
LL sum(int from, int to)
{
	return sum(to) - sum(from);
}
 
// [from, to]
void add(int from, int to, LL x)
{
	add(bit0, from, -x * (from - 1));
	add(bit1, from, x);
	add(bit0, to, x * to);
	add(bit1, to, -x);
}
 
// 压缩坐标,将坐标的值变成“这是第几种值”,返回一共有几种坐标
// x为坐标数组,长度为N
int compress(int *p)
{
	vector<int> ps(N);
 
	for (int i = 0; i < N; ++i)
	{
		ps[i] = p[i];
	}
 
	sort(ps.begin(), ps.end());
	ps.erase(unique(ps.begin(), ps.end()), ps.end());
	for (int i = 0; i < N; ++i)
	{
		p[i] = 1 + distance(ps.begin(), lower_bound(ps.begin(), ps.end(), p[i]));	// 此处加1为了防止 i += (i & -i); 永远为0
	}
	return ps.size();
}
 
int main(int argc, char *argv[])
{
	scanf("%d", &N);
	for (int i = 0; i < N; ++i)
	{
		scanf("%d%d", X + i, Y + i);
	}
	int w = compress(X);
	int h = compress(Y);
 
	for (int i = 0; i < N; ++i)
	{
		line[Y[i]].push_back(X[i]);	// 一条条扫描线
	}
 
	LL result = N;
	for (int y = 1; y <= h; ++y)
	{
		// 对每条扫描线,计算上面空白点的个数
		vector<int>& xs = line[y];	// 纵坐标为y的那些点的横坐标,一条扫描线
		sort(xs.begin(), xs.end());
		
		for (vector<int>::iterator i = xs.begin(); i != xs.end(); ++i)
		{
			int x = *i;
			LL s = sum(x - 1, x);	// 横坐标为x的空白点个数
 
			if (visited[x])
			{
				result += s;		// 就是要染色的像素点
			}
			else
			{
				visited[x] = true;
			}
			add(bit0, x, -s);		// 等同于add(x, x, -s);即对count[x] -= s;这是一个复位操作,为下次统计做准备
			if (i + 1 != xs.end())
			{
				// 到下一个黑点之间空白点的个数
				// 这两个分支合并起来就是 add(x + 1, next_x - 1, 1);的意思
				if (x + 1 < *(i + 1) - 1)			// 下一个黑子
				{
					add(x + 1, *(i + 1) - 1, 1);	// 遇到了空白
				}
				else if (x + 1 == *(i + 1) - 1)
				{
					add(bit0, x + 1, 1);			// 遇到了空白,此时等同于add(x + 1, x + 1, 1);
				}
			}
		}
	}
	cout << result << endl;

	return 0;
}

  

原文地址:https://www.cnblogs.com/jaszzz/p/12927228.html