2016 Multi-University Training Contest 1 J.Subway

Subway

Time Limit: 7000/3500 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 860    Accepted Submission(s): 126
Special Judge


Problem Description
jiefangxuanyan and yiyi cat are universally acknowledged model couples. Once jiefangxuanyan has time, he takes a train to yiyi cat’s city and meet her. This time, as usual, jiefangxuanyan gets out from the railway station, and enters the subway, only to find that the subway stations of the whole city changes their names!

As a direction idiot, jiefangxuanyan felt helpless with this situation. He called yiyi cat for help. Because the subway map is so complicated, she can’t remember it either. Fortunately, only the names of the stations changed, the structure of subway lines is the same. So she picks out the old map to make a mapping.

But mapping such a confused subway map is definitely a difficult task. So she has to use the computer. Unfortunately, she just spilt wonton soup into her computer. So, yiyi cat asked you for help, hoping you can help her with this problem.

The subway in the city forms a tree, with N subway stations and N-1 subway lines. Any pair of stations are connected with one or more subway lines. You need to find a bijective mapping from the old names to the new names, that for each pair of stations connected by exactly one line in the old map, their new names are also connected by exactly one line in the new map.
 

 

Input
The input has multiple test cases, please process to the end of file.

For each test case, the first line is an integer N(1N100000).

In the following N1 lines, each line has two space-separated string, as two stations connected by one line in the old map.

In the following N1 lines, each line has two space-separated string, as two stations connected by one line in the new map.

Station names are no longer than 10 characters, and only consists of lowercase letters (a~z).
 

 

Output
For each test case, output N lines.

Each line consists two space-separated string, as the old name and its corresponding new name.

Both the names appeared in the old and new subway map should appear exactly once in the output.

You can output the names in any order. And if there are multiple valid mappings, output any one.

Names in the old map and the new map may be the same, but this does not mean these two stations are the same.
 

 

Sample Input
3 a b b c b a a c
 

 

Sample Output
b a a b c c
 

 

Author
HIT
 

 

Source
 
 
 
题意:
    给两棵树,它们同构,要求为它们找一个双射。
题解:
    判树的同构是很经典的了。
    首先要找出树的重心。
    为了简单起见,当有两个重心时,可以在它们中间加入一个点,这样重心就唯一了。

    关于重心一般有三种说法:
    1、删掉重心后剩下的子树最大的那个最小。
    2、删掉重心后,剩下的子树大小都不超过原树的一半(floor(n/2))。
    3、删掉重心后,剩下的子树尽量平衡(最大-最小子树的大小差最小)。
    当然很容易证明,第三条是重心的性质而不是条件。
    是必要不充分的。
    而第一条和第二条是等价的。
    当有两个重心时,它们必然连在一起,且子树中最大的那个一样小。

    为了简便找出所有重心,一般使用第一条。

    找出重心后对树进行hash。
    树的hash有两种方法:
    1、弄出树的括号序列,孩子子树小的排在前面,然后做字符串hash
        在实现时无需将序列真的做出来。
    2、对每个节点,它的hashcode为
    hashcode[u] = 1 + 
                sigma(primes[i % numberOfPrimes]^i * hashcode[child[u][i]])
    实际上primes只要随便选几个数就行,实现方便。一般不会错。

  

  1 const int N = 100010;
  2 int n;
  3 
  4 struct Bijection {
  5     map<string, int> stringToInt;
  6     map<string, int>::iterator intToString[N];
  7     int tot;
  8 
  9     inline void init() {
 10         stringToInt.clear(), tot = 0;
 11     }
 12 
 13     inline int queryInt(const string &t) {
 14         if(stringToInt.count(t)) return stringToInt[t];
 15         intToString[tot] = stringToInt.insert(mk(t, tot)).first;
 16         return tot++;
 17     }
 18 
 19     inline const string& queryString(const int t) const {
 20         return intToString[t]->first;
 21     }
 22 } nameA, nameB;
 23 
 24 const int PRIMESTOT = 7;
 25 const unsigned int PRIMES[PRIMESTOT] = {
 26     313,
 27     5483, 
 28     85017, 
 29     451883, 
 30     6459271,
 31     37562047,
 32     142859339
 33 };
 34 
 35 struct cmpByHashCode {
 36     unsigned int *keys;
 37     cmpByHashCode(unsigned int *keys):keys(keys) {}
 38     inline bool operator ()(const int a, const int b) const {
 39         return keys[a] < keys[b];
 40     }
 41 };
 42 
 43 struct TreeHash {
 44     /**
 45      * 1. It will find tree's barycentre firstly and treat it as root.
 46      * 2. If it has two barycentres, it will add a new nodes between them.
 47      * 3. Then Hash Every nodes by it subtree's structure.
 48      * 4. O(n * number of primes).
 49      * 5. Choose several primes as keys to hash.
 50      * 6. The number of primes determine the accuracy of hash.
 51      * */
 52 
 53     static unsigned int factor[N];
 54 
 55     static void prepare(int N) {
 56         unsigned int tmp[PRIMESTOT];
 57         for(int i = 0; i < PRIMESTOT; ++i) tmp[i] = PRIMES[i];
 58         for(int i = 0; i < N; ++i) {
 59             factor[i] = tmp[i % PRIMESTOT];
 60             tmp[i % PRIMESTOT] *= tmp[i % PRIMESTOT];
 61         }
 62     }
 63 
 64     int head[N], son[N * 2], nex[N * 2], tot;
 65     int n;
 66     int rot, bfsList[N], father[N], size[N], maxSubtree[N];
 67     unsigned int hashCode[N];
 68     vector<int> child[N];
 69 
 70     inline void init(int m) {
 71         n = m;
 72         for(int i = 0; i < n; ++i) head[i] = -1;
 73         tot = 0;
 74     }
 75 
 76     inline void addEdge(int u, int v) {
 77         son[tot] = v, nex[tot] = head[u];
 78         head[u] = tot++;
 79     }
 80 
 81     inline void bfs(int st) {
 82         int len = 0;
 83         bfsList[len++] = st, father[st] = -1;
 84         for(int idx = 0; idx < len; ++idx) {
 85             int u = bfsList[idx];
 86             for(int tab = head[u], v; tab != -1; tab = nex[tab])
 87                 if(father[u] != (v = son[tab]))
 88                     father[v] = u, bfsList[len++] = v;
 89         }
 90     }
 91 
 92     inline int getBarycentre() {
 93         bfs(0);
 94 
 95         for(int i = 0; i < n; ++i) size[i] = 0;
 96         for(int i = n - 1; i >= 0; --i) {
 97             int u = bfsList[i];
 98             ++size[u];
 99             if(father[u] != -1) size[father[u]] += size[u];
100 
101             maxSubtree[u] = n - size[u];
102             for(int tab = head[u], v; tab != -1; tab = nex[tab])
103                 if(father[u] != (v = son[tab]))
104                     maxSubtree[u] = max(maxSubtree[u], size[v]);
105         }
106 
107         int rot = 0;
108         for(int i = 1; i < n; ++i)
109             if(maxSubtree[rot] > maxSubtree[i]) rot = i;
110         int anotherRot = -1;
111         for(int i = 0; i < n; ++i)
112             if(i != rot && maxSubtree[rot] == maxSubtree[i]) {
113                 anotherRot = i;
114                 break;
115             }
116 
117         if(anotherRot != -1) {
118             int newRot = n++;
119             head[newRot] = -1;
120             addEdge(newRot, rot), addEdge(newRot, anotherRot);
121 
122             for(int tab = head[rot]; tab != -1; tab = nex[tab])
123                 if(son[tab] == anotherRot) {
124                     son[tab] = newRot;
125                     break;
126                 }
127             for(int tab = head[anotherRot]; tab != -1; tab = nex[tab])
128                 if(son[tab] == rot) {
129                     son[tab] = newRot;
130                     break;
131                 }
132 
133             rot = newRot;
134         }
135         return rot;
136     }
137 
138     inline int hashTree() {
139         int rot = getBarycentre();
140         bfs(rot);
141 
142         for(int i = n - 1; i >= 0; --i) {
143             int u = bfsList[i];
144 
145             child[u].clear(), hashCode[u] = 1;
146             for(int tab = head[u], v; tab != -1; tab = nex[tab])
147                 if(father[u] != (v = son[tab])) child[u].push_back(v);
148             sort(all(child[u]), cmpByHashCode(hashCode));
149             for(int idx = 0; idx < sz(child[u]); ++idx)
150                 hashCode[u] += factor[idx] * hashCode[child[u][idx]];
151         }
152         return rot;
153     }
154 } treeA, treeB;
155 unsigned int TreeHash::factor[N];
156 
157 inline void init() {
158     nameA.init(), nameB.init(), treeA.init(n), treeB.init(n);
159 }
160 
161 inline void buildBijection(int u, int v) {
162     if(u < n && v < n)
163         cout << nameA.queryString(u) << ' ' << nameB.queryString(v) << "\n";
164     for(int i = 0; i < sz(treeA.child[u]); ++i)
165         buildBijection(treeA.child[u][i], treeB.child[v][i]);
166 }
167 
168 inline void solve() {
169     int u = treeA.hashTree(), v = treeB.hashTree();
170     buildBijection(u, v);
171 }
172 
173 int main() {
174     ios::sync_with_stdio(false);
175     cin.tie(NULL);
176     TreeHash::prepare(N);
177     while(cin >> n) {
178         init();
179         for(int i = 1; i < n; ++i) {
180             string a, b;
181             cin >> a >> b;
182             int u = nameA.queryInt(a), v = nameA.queryInt(b);
183             treeA.addEdge(u, v), treeA.addEdge(v, u);
184         }
185         for(int i = 1; i < n; ++i) {
186             string a, b;
187             cin >> a >> b;
188             int u = nameB.queryInt(a), v = nameB.queryInt(b);
189             treeB.addEdge(u, v), treeB.addEdge(v, u);
190         }
191         solve();
192     }
193     return 0;
194 }
View Code

 

 

 
posted @ 2016-08-28 20:35  yanzx6  阅读(276)  评论(0编辑  收藏  举报