题目链接:http://poj.org/problem?id=1470
题目大意:
给你一棵树,然后求公共祖先。
只不过我们需要记录每个节点作为公共祖先的次数。然后升序输出。
解题思路:
这道题,我不想做啥评论了。。。。。。。。。你妹啊!!!!!!!!!为了你,WA了20多次啊啊啊啊啊啊啊啊啊啊啊啊!!!!!!!!
原来Wrong的代码,数组开大一点点就过了啊。。。。。。。原来WA的代码,换成G++就过了啊!!!!!!!!!!!!!
因为什么啊!!!!!!!!!!!!!
2天都被你浪费了啊!!!!
好了,发泄完毕。
这道题需要注意的问题有:
1.数据第一个不是这棵树的根,所以我们需要寻找这棵树的根,这个很好解决,在输入的时候记录每个节点的入度,那么入度为0的点就是树根。
2.测试数据有多组。
3.输入比较变态,但是处理方法很多,貌似还有scanf正则表达式的。。Orz,我用的简单的一招,哈哈。轻松水过。
4.虽然只有900个点,但是数组不要开小,我原来开的1000不过,后来开到4000就过了。郁闷。
代码如下:
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<vector> #include<cmath> using namespace std; #define N 4010 #define M 4010 int visit[M], level[M], first[N]; //深搜步骤、层、首次出现 int dp[20][N]; //RMQ-dp处理 vector<int>son[N]; //记录孩子 int in[N]; //入度 int top; int res[N]; //结果 void DFS(int ceng, int now) { if(first[now] == -1) first[now] = top; level[top] = ceng; visit[top++] = now; if(son[now].size() == 0) //终止条件 return ; for(int i = 0; i < son[now].size(); ++i) { DFS(ceng + 1, son[now][i]); level[top] = ceng; visit[top++] = now; } } void RMQ(int n) //预处理 { for(int i = 1; i <= n; ++i) dp[0][i] = level[i]; for(int i = 1; (1 << i) <= n; ++i) for(int j = 1; j <= n + 1 - (1 << i); ++j) dp[i][j] = min(dp[i - 1][j], dp[i - 1][j + (1 << i >> 1)]); } int ac(int start, int end) //返回最近公共祖先 { int k = (int)(log((double)(end - start + 1.0)) / log(2.0)); int temp = min(dp[ k ][ start ], dp[ k ][ end - (1 << k) + 1 ]); for(int i = start; i <= end; ++i) //找下标 if(level[i] == temp) return visit[i]; } void init(int n) { top = 1; memset(in, 0, sizeof(in)); memset(first, -1, sizeof(first)); memset(res, 0, sizeof(res)); for(int i = 1; i <= n; ++i) son[i].clear(); } int main() { int vnum; int a, num, b; int root; int query, start, end; while(scanf("%d", &vnum) != EOF) { init(vnum); //初始化 for(int i = 1; i <= vnum; ++i) //读入图 { scanf("%d:(%d)", &a, &num); while(num--) { scanf("%d", &b); son[a].push_back(b); in[b]++; //入度+1 } } for(root = 1; in[root]; ++root); //找根 DFS(0, root); //深搜 RMQ(2 * vnum - 1); //O(nlogn)预处理 scanf("%d", &query); while(query--) { scanf(" (%d %d) ", &start, &end); start = first[start], end = first[end]; //找到在level中的位置 if(start > end) //保证前小后大 swap(start, end); res[ac(start, end)]++; } for(int i = 1; i <= vnum; ++i) if(res[i]) printf("%d:%d\n", i, res[i]); } return 0; }