首页 > 技术文章 > codeforces 501C. Misha and Forest 解题报告

windysai 2015-02-27 15:24 原文

题目链接:http://codeforces.com/problemset/problem/501/C

题目意思:有 n 个点,编号为 0 ~ n-1。给出 n 个点的度数(即有多少个点跟它有边相连)以及跟它相连的点的编号的异或结果。最后需要输出整幅图的所有边的情况。

  这道题确实是一道很好的题目!!!!它说拓扑排序的变形,需要队列的运用,还有就是异或计算的性质!!!(非一般厉害)

  由于是无向无环的简单图,换言之就是一棵树啦^_^。那么它就肯定有叶子结点,叶子节点的度数为1,此时它相邻点的异或结果实际上就是所求点的编号了。然后把跟叶子节点相邻的点的度数-1,代表把叶子节点去除,此时异或结果是有变的。需要用本来的异或结果跟该叶子节点再异或一次,就得出除了这个叶子节点外其他点的异或值了。举个例子吧,假如有一幅图是这样的。

            

       0 的相邻点有1、 2、 3,异或出来的结果是0,它的度数是3.那么当处理0-1这条边时,容易知道去除1这个点后,只有2和3异或了:10 ^ 11 = 1,刚好等于 1 ^ 2 ^ 3 ^ 1 (01 ^ 10 ^ 11 ^ 01)。异或的一个性质就是a^b^c^a = b ^ c。是不是很神奇呢~~~~当然我们总是处理那些度数为1的点,把这些点放入队列里面,依次处理。

  

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 #include <queue>
 5 using namespace std;
 6 
 7 #define f first
 8 #define s second
 9 
10 const int maxn = (1<<16) + 5;
11 int degree[maxn], XOR_sum[maxn];
12 
13 int main()
14 {
15     #ifndef ONLINE_JUDGE
16         freopen("in.txt", "r", stdin);
17     #endif // ONLINE_JUDGE
18 
19     int n;
20     while (scanf("%d", &n) != EOF) {
21         queue<int> q;
22         pair<int, int> ans[maxn];
23         for (int i = 0; i < n; i++) {
24             scanf("%d%d", &degree[i], &XOR_sum[i]);
25             if (degree[i] == 1)
26                 q.push(i);
27         }
28         int cnt = 0;
29         while (!q.empty()) {
30             int from = q.front();
31             q.pop();
32             if (degree[from] == 1) {   // 这句判断很重要,因为有可能degree[]--过程中使得变为0
33                 int to = XOR_sum[from];
34                 ans[cnt].f = from;
35                 ans[cnt++].s = to;
36                 degree[to]--;
37                 XOR_sum[to] ^= from;   // 异或性质
38                 if (degree[to] == 1)
39                     q.push(to);
40             }
41         }
42         printf("%d\n", cnt);
43         for (int i = 0; i < cnt; i++)
44             printf("%d %d\n", ans[i].f, ans[i].s);
45     }
46     return 0;
47 }

 

推荐阅读