zoukankan      html  css  js  c++  java
  • 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;
    }
    

      

  • 相关阅读:
    数组
    做了个进制转换图
    类的练习
    3.10l练习
    c#学习第二课
    c#第四课习题
    c#学习第三课
    学习PHP&MYSQL之——字符编码篇(一)
    中缀表达式转换成后缀表达式
    模板方法模式(Template Pattern)
  • 原文地址:https://www.cnblogs.com/jaszzz/p/12927228.html
Copyright © 2011-2022 走看看