POJ 2513 字典树
题意:
题目给你了一些木棒,这些木棒前后有颜色,现在问你可不可能将这些接起来按照木棒的颜色来连接。题目的木棒数量是25w。
思路:
我们将这些木棒的端点看成图里面的端点,木棒本身作为图的边,那么这时候这道题就转化成求这些木棒(边)组成的图里面是否有存在欧拉图。
欧拉图:通过图中的所有边并且只通过一次的通路为欧拉图,图为有向图或无向图。
那么无向图是欧拉图的充要条件是:
- 图是连通的
- 图中存在欧拉通路当且仅当图中的节点仅有零个或两个度为奇数的节点。
此时问题就经过转化了。然后我们明确两个任务:
- 判断是否存在多个连通分量
- 存在多少个度数为奇数的节点
现在来解决第一个问题:判断是否存在多个连通分量
如果把这些点先看成n颗树,那么如果能够连接起来就代表这些点属于一个根,也就是连上其他的树。那么如何表示这个关系呢—-并查集。我们可以利用并查集来先让这些节点作为单独的树,然后若是一根木棒,那么就把这两个节点连接在一起。
第二个问题:存在多少个度数为奇数的节点
怎样让这些节点标号呢,如果我们用map容器,那么根据前人的经验,这个是不行的,看来POJ专门卡STL。于是我们首先要将这些节点进行标号,此时采用Trie,能根据前缀快速的定位这个单词是否出现过。这就是为什么这道题需要采用字典树的原因,并且这个字典树需要动态进行建树。将节点标号后需要就在对应的degree数组里面进行++。
#include<iostream>
#include<string>
using namespace std;
#define maxn 500100
int tree[maxn];
int color;
int degree[maxn];
typedef struct node
{
bool flag;
struct node *next[27];
int id;
node()
{
id = 0;
flag = false;
memset(next,0, sizeof(next));
}
}Trie;
int find(int x)
{
if (tree[x] != x)
tree[x] = find(tree[x]);
return tree[x];
}
void merge(int a, int b)
{
int fx = find(a), fy = find(b);
if (fx != fy)
tree[fx] = fy;
}
int insert(Trie *root,string word)
{
Trie *p = root;
int i = 0;
while (word[i] != '\0')
{
int index = word[i] - 'a';
if (!p->next[index])
p->next[index] = new Trie;
p = p->next[index];
i++;
}
if (p->flag)
return p->id;
else
{
p->flag = true;
p->id = ++color;
return p->id;
}
}
void init()
{
for (int i = 0; i < maxn; i++)
tree[i] = i;
}
int main()
{
init();
string a, b;
Trie *root = new Trie;
while (cin >> a >> b)
{
int i = insert(root,a);
int j = insert(root,b);
degree[i]++;
degree[j]++;
merge(i, j);
}
int s = find(1);
int num = 0;
for (int i = 1; i <= color; i++)
{
if (degree[i] % 2 == 1)
num++;
if (num > 2)
{
cout << "Impossible" << endl;
return 0;
}
if (find(i) != s)
{
cout << "Impossible" << endl;
return 0;
}
}
if (num == 1)
cout << "Impossible" << endl;
else
cout << "Possible" << endl;
return 0;
}