2017百度之星初赛B-1002(HDU-6115)
2017-08-16 09:33
337 查看
一、思路
这题“看似”比较难搞的一点是,一个节点上有多个办公室,这怎么求?其他的,求树中任意两个节点的距离(注意:没有最远或最最进这一说法,因为树上任意两个节点之间有且仅有一条路径。不然就有回路了,对吧。)都不是特别难的问题。
(1)如何找到任意两个公司之间的最短距离?没有很好的办法,只能暴力。记录每个公司的每个办公室所在节点,求指定两个公司A和B的最短距离时,枚举公司A的所有办公室所在节点,再枚举公司B的所有办公室所在节点,求出被枚举的节点之间的距离,然后,取最小的那一个输出。
(2)求树中任意两个节点的距离?我一开始用的是Tarjan求LCA的算法,然后,交上去TLE了两发。后来换了一种策略,记录每个节点的深度、它的父节点以及它到父节点之间的距离。求任意两个点的LCA的时候,采用谁深度大谁先上移的方法,然后移动过程中记录移动了多少距离,当两个点移动到一个点时,这个点就是他们的LCA,返回记录的距离即可。这样,可以避免求子节点到根节点的距离(即麻烦又要多写很多不必要的代码)。另外一点,要不要先求出树的重心来优化时间复杂度,我用第二种策略提交的第一份AC的代码,求了树重心,耗时:8595MS;后来直接把求重心的代码去掉,直接把1号节点当总根节点,耗时:8782MS,差不多。所以,我下面贴的代码就没求树的重心了,这样代码也简洁了好多,关键函数就只有一个:找到给定两个节点的LCA并返回它们之间的距离。
二、代码
这题“看似”比较难搞的一点是,一个节点上有多个办公室,这怎么求?其他的,求树中任意两个节点的距离(注意:没有最远或最最进这一说法,因为树上任意两个节点之间有且仅有一条路径。不然就有回路了,对吧。)都不是特别难的问题。
(1)如何找到任意两个公司之间的最短距离?没有很好的办法,只能暴力。记录每个公司的每个办公室所在节点,求指定两个公司A和B的最短距离时,枚举公司A的所有办公室所在节点,再枚举公司B的所有办公室所在节点,求出被枚举的节点之间的距离,然后,取最小的那一个输出。
(2)求树中任意两个节点的距离?我一开始用的是Tarjan求LCA的算法,然后,交上去TLE了两发。后来换了一种策略,记录每个节点的深度、它的父节点以及它到父节点之间的距离。求任意两个点的LCA的时候,采用谁深度大谁先上移的方法,然后移动过程中记录移动了多少距离,当两个点移动到一个点时,这个点就是他们的LCA,返回记录的距离即可。这样,可以避免求子节点到根节点的距离(即麻烦又要多写很多不必要的代码)。另外一点,要不要先求出树的重心来优化时间复杂度,我用第二种策略提交的第一份AC的代码,求了树重心,耗时:8595MS;后来直接把求重心的代码去掉,直接把1号节点当总根节点,耗时:8782MS,差不多。所以,我下面贴的代码就没求树的重心了,这样代码也简洁了好多,关键函数就只有一个:找到给定两个节点的LCA并返回它们之间的距离。
二、代码
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<iostream> #include<cstdio> #include<cstring> #include<vector> using namespace std; const int INF = 0x3fff3fff; const int MAXN = 100010; typedef struct { int to, wt, next; } Edge; Edge tree[MAXN * 2]; int head[MAXN], cnt; int n, m, q; /**office[i][j]:公司i的第j个办公室所在节点。*/ vector<int> office[MAXN]; void add(int from, int to, int wt) { tree[cnt].to = to; tree[cnt].wt = wt; tree[cnt].next = head[from]; head[from] = cnt++; } void init() { memset(head, -1, sizeof(head)); cnt = 0; for(int i = 0; i < MAXN; ++i)office[i].clear(); } /** deep[i]:i节点的深度。 f[i]:i节点的父节点。 len2f[i]:i节点到父节点的长度。 */ int deep[MAXN], f[MAXN], len2f[MAXN]; /** d:深度。总根(树的重心)节点的深度为0。 */ void dfsDep(int root, int par, int d) { deep[root] = d; f[root] = par; for(int i = head[root], to = -1; i != -1; i = tree[i].next) { to = tree[i].to; if(to != par) { len2f[to] = tree[i].wt; dfsDep(to, root, d + 1); } } } int getDis(int a, int b) { int res = 0; while(deep[a] < deep[b]) { res += len2f[b]; b = f[b]; } while(deep[a] > deep[b]) { res += len2f[a]; a = f[a]; } while(a != b) { res += len2f[a] + len2f[b]; a = f[a]; b = f[b]; } return res; } int main() { #ifndef ONLINE_JUDGE freopen("input.txt", "r", stdin); #endif // ONLINE_JUDGE int t, a, b, c; scanf("%d", &t); while(t--) { init(); scanf("%d%d", &n, &m); for(int i = 1; i < n; ++i) { scanf("%d%d%d", &a, &b, &c); add(a, b, c); add(b, a, c); } for(int i = 1; i <= m; ++i) { scanf("%d", &a); for(int k = 0; k < a; ++k) { scanf("%d", &b); office[i].push_back(b); } } dfsDep(1, -1, 0); scanf("%d", &q); for(int i = 1; i <= q; ++i) { scanf("%d%d", &a, &b); c = INF; for(int j = 0, asz = office[a].size(); j < asz; ++j) { for(int k = 0, bsz = office[b].size(); k < bsz; ++k) { c = min(c, getDis(office[a][j], office[b][k])); if(c == 0) {j = asz; break;} } } printf("%d\n", c); } } return 0; }
相关文章推荐
- 2017百度之星初赛B-1002(HDU-6115)
- 2017百度之星初赛:A-1002. 数据分割(并查集+set)
- HDU 6114 Chess(DP)2017百度之星初赛(B) 1001
- hdu 6119/2017百度之星初赛B——小小粉丝度度熊(尺取/贪心)
- 2017百度之星初赛(B) 1002 Factory(倍增法求LCA)
- HDU 6108 小C的倍数问题 (数论 2017百度之星初赛A第1题)
- 2016"百度之星" - 初赛(Astar Round2A)1002 / HDU 5691 状态压缩DP
- hdu 5247 找连续数 && 百度之星初赛第一场1002
- 2017百度之星初赛A-1006(HDU-6113)
- 2017百度之星初赛A-1006(HDU-6113)
- HDU 6108 2017 百度之星 初赛A 1001 小C的倍数问题
- HDU 6112 今夕何夕 (日历题 Zeller公式 2017百度之星初赛A第五题)
- (最小费用流)hdu 6118(2017百度之星初赛B 1005) 度度熊的交易计划
- HDU-2017"百度之星"程序设计大赛-初赛(B)-1002-Factory
- HDU_【2017 Multi-University Training Contest 1】——1002.Balala Power!
- 百度之星 2015 初赛(1) 1002 找连续数
- 2017百度之星初赛(A) 1005 今夕何夕(阅读题)
- 百度之星2017初赛A-1005-今夕何夕
- HDU 6108 小C的倍数问题 【数学】 (2017"百度之星"程序设计大赛 - 初赛(A))
- HDU 6109 数据分割 【并查集+set】 (2017"百度之星"程序设计大赛 - 初赛(A))