放牧代码和思想
专注自然语言处理、机器学习算法
    愛しさ 優しさ すべて投げ出してもいい

AOJ 2170: Marked Ancestor 题解 《挑战程序设计竞赛》

AOJ 2170: Marked Ancestor

染色祖先:距离某节点最近的染色节点称为祖先,求染色过程中各节点祖先。

2.4 加工并储存数据的数据结构

并查集

稍有些复杂,需要bfs和两棵树。一棵树用来维护并查集,一棵树用来保存原来的树。bfs用来求解每个节点的祖先。也许看到这里你很好奇:既然节点的祖先是用bfs求出来的,那并查集是干什么吃的?这题的复杂之处在于,它是多用例,如果每染色一个节点就跑一遍bfs,那么会TLE。并查集用来省时间的。

思路是先染色,同时用个栈保留这些操作。接着跑一遍bfs,得到祖先的最终状态,并且将祖先相同的节点放入同一个集合中。然后从栈里面弹出操作将节点染回来,“反染色”的过程中,更新并查集和祖先。

注意两点:

  1. 染色节点的祖先是自己

  2. 可能存在重复染色,忽略它们

#include <iostream>
#include <vector>
#include <queue>
#include <stack>
using namespace std;

// 并查集相关数据与算法
#define MAX_N 100000 + 16
int parent[MAX_N];
int height[MAX_N];

void init(const int& n)
{
	for (int i = 0; i < n; ++i)
	{
		parent[i] = i;
		height[i] = 0;
	}
}

int find(const int& x)
{
	if (parent[x] == x)
	{
		return x;
	}
	else
	{
		return parent[x] = find(parent[x]);
	}
}

void unite(int x, int y)
{
	x = find(x);
	y = find(y);
	if (x == y)
	{
		return;
	}

	if (height[x] < height[y])
	{
		parent[x] = y;
	}
	else
	{
		parent[y] = x;
		if (height[x] == height[y])
		{
			++height[x];
		}
	}
}

bool same(const int& x, const int& y)
{
	return find(x) == find(y);
}
// End Of 并查集

// 原始的树用它来描述
vector<int> children[MAX_N];
int parent_tree[MAX_N];
bool marked[MAX_N];
int ancestor[MAX_N];
// 每条指令被拆分为 操作 + 目标
stack<char> operation;
stack<int>  target;

void bfs(int index, int the_ancestor)
{
	queue<int> q_index;
	queue<int> q_ancestor;
	q_index.push(index);
	q_ancestor.push(the_ancestor);
	while (!q_index.empty())
	{
		the_ancestor = q_ancestor.front(); q_ancestor.pop();
		index = q_index.front(); q_index.pop();
		if (marked[index] == true)
		{
			the_ancestor = index;
		}
		ancestor[index] = the_ancestor;
		for (vector<int>::iterator it = children[index].begin(); it != children[index].end(); ++it)
		{
			q_index.push(*it);
			q_ancestor.push(the_ancestor);
		}
	}
}

///////////////////////////SubMain//////////////////////////////////
int main(int argc, char *argv[])
{

	int N, Q;
	while (cin >> N, cin >> Q, N)
	{
		for (int i = 0; i < N; ++i)
		{
			children[i].clear();
			marked[i] = false;
		}
		marked[0] = true;
		int p;
		for (int i = 1; i < N; ++i)
		{
			cin >> p; --p;
			parent_tree[i] = p;
			children[p].push_back(i);
		}
		for (int i = 0; i < Q; ++i)
		{
			char o;
			int t;
			cin >> o >> t; --t;
			if (o == 'M')
			{
				if (marked[t])
				{
					continue;
				}
				else
				{
					marked[t] = true;
				}
			}
			operation.push(o);
			target.push(t);
		}
		bfs(0, 0);
		init(N);
		for (int i = 0; i < N; ++i)
		{
			unite(i, ancestor[i]);
		}
		unsigned long long result = 0;
		while (!operation.empty())
		{
			char o = operation.top(); operation.pop();
			int t = target.top(); target.pop();
			if (o == 'Q')
			{
				result += ancestor[find(t)] + 1;	// 题目index从1开始
			}
			else
			{
				// 执行“反染色”操作,之后这个节点的祖先变为其父节点的祖先
				int p = ancestor[find(parent_tree[t])];
				unite(t, parent_tree[t]);
				ancestor[find(t)] = p;
			}
		}
		cout << result << endl;
	}
	

	return 0;
}
///////////////////////////End Sub//////////////////////////////////

速度还行,从上表看出,AOJ竟然支持 C++11 了!而且与 C++ 视为两种不同的语言!看来第五版的《C++ Primary》里说C++11是一种全新的语言真是诚不我欺也!

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » AOJ 2170: Marked Ancestor 题解 《挑战程序设计竞赛》

评论 3

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #2

    求您认真去计算一下复杂度,别在这里无知。最坏情况1e10

    无名8年前 (2016-01-15)回复
  2. #1

    请问做题用的是什么工具啊?

    传道者9年前 (2015-08-03)回复

我的作品

HanLP自然语言处理包《自然语言处理入门》