2017百度之星初赛(B) 1002 Factory(倍增法求LCA)

来源:互联网 发布:ens域名能做什么 编辑:程序博客网 时间:2024/05/16 18:57

题目链接:点击打开链接

思路:

树形图上的最短距离,很容易想到LCA算法,树形图上的两个节点的最短距离是唯一的、确定的,是两个节点到其最近公共祖先的距离之和。直接枚举两个分公司的办公室,两两算出最短路径,取最小值即为结果。

具体利用LCA求出最短距离的方法是,先预处理,深搜一遍树形图,得到每个节点距离树根的距离,则最短距离为dist[x] + dist[y] - 2 * dist[ lca(x,y)]。

此处LCA的具体实现采用倍增法,还可以采用dfs + ST表,都为在线算法,实时查询。感觉此题用Tarjan离线算法不合适,LCA算法进行前要存储所有查询,此题事先存储要查询的点对不光麻烦,更可怕的是,存储量最坏会达到10^10级别,会超内存。

// Factory.cpp 运行/限制:858ms/10000ms(对,没看错,就是10000......)#include <cstdio>#include <cstring>#include <algorithm>#include <iostream>#include <vector>using namespace std;#define INF 0X3f3f3f3fint n;int cnt, head[100005];int depth[100005], dist[100005];//深度,距离根节点距离int parent[20][100005];//parent[i][v]表示v节点往上走2^i步到达的顶点vector<int> office[100005];//每个分公司的办公室所在城市struct node {int to, next, weight;}edge[200010];void add(int x, int y, int w) {//树加边cnt++;edge[cnt].to = y;edge[cnt].weight = w;edge[cnt].next = head[x];head[x] = cnt;}void dfs(int x, int fa) {//搜索得每个点的深度,与根节点的距离parent[0][x] = fa;for (int i = head[x]; i; i = edge[i].next) {int y = edge[i].to;if (y == fa) continue;depth[y] = depth[x] + 1;dist[y] = dist[x] + edge[i].weight;dfs(y, x);}}void init() {//预处理,打2^k表memset(depth, 0, sizeof(depth));memset(dist, 0, sizeof(dist));memset(parent, 0, sizeof(parent));dfs(1, 0);for (int i = 0; i + 1 < 20; i++) {for (int j = 1; j <= n; j++) {if (!parent[i][j]) parent[i + 1][j] = 0;else parent[i + 1][j] = parent[i][parent[i][j]];}}}int lca(int x, int y) {//LCAif (depth[x] > depth[y]) swap(x, y);for (int i = 0; i < 20; i++) {if ((depth[y] - depth[x]) >> i & 1) {y = parent[i][y];}}if (x == y) return x;for (int i = 19; i >= 0; i--) {if (parent[i][x] != parent[i][y]) {x = parent[i][x];y = parent[i][y];}}return parent[0][x];}int main(){int t, m, q;int x, y, w;scanf("%d", &t);while (t--) {scanf("%d%d", &n, &m);cnt = 0;memset(head, 0, sizeof(head));for (int i = 1; i < n; i++) {scanf("%d%d%d", &x, &y, &w);add(x, y, w);add(y, x, w);}for (int i = 1; i <= m; i++) {int count, num;scanf("%d", &count);for (int j = 0; j < count; j++) {scanf("%d", &num);office[i].push_back(num);}}init();scanf("%d", &q);for (int i = 0; i < q; i++) {int re = INF;scanf("%d%d", &x, &y);//枚举x,y的所有办公室,两两求距离for (int j = 0; j < office[x].size(); j++) {for (int t = 0; t < office[y].size(); t++) {int a = office[x][j];int b = office[y][t];re = min(re, dist[a] + dist[b] - 2 * dist[lca(a,b)]);}}printf("%d\n", re);}for (int i = 1; i < 100005; i++) {office[i].clear();}}    return 0;}