题意:给出n个两个端点有颜色的木棒。将端点颜色相同的木棒接起来,问能否接成一条直线。
思路:欧拉路径问题:能否找出一条路径,使每条边都经过一次。在这个题中,我们将每个颜色看做一个点,木棒看成无向边,就形成了一个欧拉路径。
欧拉路径有解的条件:1.整个图是连通的。2.有0个或2个度为奇数的点,其他的点的度都是偶数。
第一个条件可以利用并查集来判定图的连通性。第二个条件可以统计每个点的度,来进行判定。
但是这个题的难点在于如果何将表示字符串的颜色,映射成一个点?如果用map的话,可以发现,会TLE。必须另想办法。
是不是想到了字典树呢? 所以思路就出来了。
代码如下:
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
struct Trie{
static const int CHARSET = 26;
static const int BASE = 'a';
static const int MAXNODE = 4000000;
int child[MAXNODE][CHARSET];
int v[MAXNODE];
int root,sz;
int id;
Trie(){init();}
void init(){
root = 1,sz = 1,id = 1;
memset(child[1],0,sizeof(child[1]));
memset(v,0,sizeof(v));
}
int insert(char * s){
int * cur = & root;
for(char *p = s; *p; ++p){
cur = &child[*cur][*p - BASE];
if(*cur == 0){
*cur = ++sz;
memset(child[*cur],0,sizeof(child[*cur]));
}
}
if(v[*cur] != 0)
return v[*cur];
else
return v[*cur] = id++;
}
};
struct DisjointSet{
vector<int> father,rank;
int cnt;
DisjointSet(int n):father(n+1),rank(n+1),cnt(n){
for(int i = 1; i <= n; ++i)
father[i] = i;
}
int find(int x){
return father[x] = father[x] == x? x: find(father[x]);
}
void unite(int x,int y){
x = find(x), y = find(y);
if(x != y){
cnt--;
if(rank[x] > rank[y])
father[y] = x;
else{
father[x] = y;
if(rank[x] == rank[y])
rank[y]++;
}
}
}
};
struct edge{
int x,y;
edge(int a =0, int b = 0):x(a),y(b){}
};
Trie trie;
vector<edge> G;
char s1[20],s2[20];
int c[500100];
int main(void)
{
//freopen("input.txt","r",stdin);
while(scanf("%s %s",s1,s2) != EOF){
int x = trie.insert(s1);
int y = trie.insert(s2);
c[x]++,c[y]++;
//printf("%d %d\n",x,y);
G.push_back(edge(x,y));
}
int n = trie.id-1;
if(n == 0){
printf("Possible\n");
return 0;
}
DisjointSet s(n);
for(int i = 0,sz = G.size(); i < sz; ++i){
if(s.find(G[i].x) != s.find(G[i].y))
s.unite(G[i].x,G[i].y);
}
int cnt = 0;
for(int i = 1; i <= n; ++i)
if(c[i] & 1) cnt++;
//printf("%d %d\n",cnt,s.cnt);
if((cnt == 0 || cnt == 2) && s.cnt == 1)
printf("Possible\n");
else
printf("Impossible\n");
return 0;
}