题目来源:http://poj.org/problem?id=2513
题目大意:
给你n根木棒,每个木棒两端都涂有颜色,任意两根木棒可以连在一起,如果他们有一端颜色是相同的,问你最后是否能把所有的木棒连成一根。
题目分析:
这道题是一个欧拉通路的判断,但是令人纠结的是,节点不是整数而是字符串,很头疼啊= =
如果不了解欧拉通路可以自己百度一下。。
因为节点是字符串,所以我们要把它转换成整数的节点,这就涉及到字符串的查找,但是map在这题是会超时的。。。所以想到可以用AC自动机
代码实现:
#include <cstdio>
#include <cstring>
using namespace std;
typedef char char_t;//字符的类型,需要的话可以改成int等
const int SIGMA=26;//字符集的大小
const int ELEMENT_MAX=5000100;//trie图中最多可能的节点数,用于内存管理
#define MAX 500010
int father[MAX],rank[MAX];
int ID;
char s1[MAX/2][15],s2[MAX/2][15];
int d[MAX];
int degree[MAX];
bool vis[MAX];
int len1[MAX/2],len2[MAX/2];
struct TrieGraph
{
struct trie
{
bool match;
int id;
trie *pre,*child[SIGMA];
trie():match(false),pre(0)
{
memset(child,0,sizeof(child));
id = -1;
}
void* operator new(size_t, void *p)
{
return p;
}
} root,superroot;
static char storage[ELEMENT_MAX*sizeof(trie)];
static trie* data;
static void init()
{
data=(trie*)storage;
}
void insert(char_t* s,int n) //在trie中插入一个长度为n的字符串s
{
//如果需要在trie节点中记录某些信息,一般是在这个函数里添加
trie* t=&root;
for(int i=0; i<n; ++i)
{
char_t c=s[i];
if(!t->child[c])
{
t->child[c]=new((void*)data++) trie;
if(i == n-1)
{
ID++;
}
}
t=t->child[c];
}
if(!t->match)
{
t->match=true;
t->id = ID;
}
}
void build_graph() //所有的插入完毕后,将trie树扩充为图
{
static trie* Q[ELEMENT_MAX];
superroot.pre=root.pre=&superroot;
for(int i=0; i<SIGMA; ++i)
superroot.child[i]=&root;
int head=0,tail=0;
Q[tail++]=&root;
while(head!=tail)
{
trie* t=Q[head++];
t->match|=t->pre->match;
for(int i=0; i<SIGMA; ++i)
{
if(t->child[i])
{
t->child[i]->pre=t->pre->child[i];
Q[tail++]=t->child[i];
}
else
t->child[i]=t->pre->child[i];
}
}
}
int match(char_t* s,int n) //返回长度为n的字符串s中有否已被插入的字符串的匹配
{
trie* t=&root;
for(int i=0; i<n; ++i)
{
t=t->child[s[i]];
}
return t->id;
}
};
char TrieGraph::storage[ELEMENT_MAX*sizeof(trie)];
TrieGraph::trie* TrieGraph::data;
void make_set()
{
int i;
for(int i=1; i<=MAX/2; i++)
{
rank[i] = 0;
father[i] = i;
}
}
int find_set(int x)
{
if(x != father[x])
{
father[x] = find_set(father[x]);
}
return father[x];
}
void union_set(int x,int y)
{
x = find_set(x);
y = find_set(y);
if(rank[x] > rank[y])
father[y] = x;
else
{
father[x] = y;
if(rank[x] == rank[y])
{
rank[y]++;
}
}
}
int main()
{
TrieGraph::init();
TrieGraph g;
ID = 0;
int k=0;
int cnt = 0;
memset(degree,0,sizeof(degree));
memset(vis,0,sizeof(vis));
make_set();
while(scanf("%s %s",s1[k],s2[k]) != EOF)
{
//printf("%s %s\n",s1[k],s2[k]);
len1[k]=strlen(s1[k]);
for(int i=0; i<len1[k]; ++i)
s1[k][i]-='a';
g.insert(s1[k],len1[k]);
len2[k]=strlen(s2[k]);
for(int i=0; i<len2[k]; ++i)
s2[k][i]-='a';
g.insert(s2[k],len2[k]);
k++;
}
g.build_graph();
int ii=0;
for(int i=0; i<k; ++i)
{
//printf("%d %d\n",len1[i],len2[i]);
int d1 = g.match(s1[i],len1[i]);
int d2 = g.match(s2[i],len2[i]);
if(!vis[d1])
{
d[ii++] = d1;
vis[d1] = true;
}
if(!vis[d2])
{
d[ii++] = d2;
vis[d2] = true;
}
degree[d1]++;
degree[d2]++;
union_set(d1,d2);
}
for(int i=0; i<ii; i++)
{
if(degree[d[i]] % 2 == 1)
cnt++;
}
memset(vis,false,sizeof(vis));
int ans = 0;
if(cnt==1 || cnt >= 3)
{
printf("Impossible\n");
}
else
{
int fanum=0;
for(int i=0; i<ii; i++)
{
int fa = find_set(d[i]);
//printf("%d %d\n",d[i],fa);
if(!vis[fa])
{
vis[fa] = true;
ans++;
}
}
if(ans > 1)
printf("Impossible\n");
else
printf("Possible\n");
}
return 0;
}