[USACO10HOL]牛的政治Cow Politics
题目链接:[USACO10HOL]牛的政治Cow Politics
题目大意
给你\(n\)个关系,再给你一共有多少个群落,每个关系包含两个内容,在哪个群落以及当前 \(i\) 节点的父亲节点是谁。然后让你求每个群落距离最远的两个点的距离是多大
题目题解
刚开始想的是树的直径,但看了下样例发现都是在一棵树上进行的,搜肯定不怎么好搜,想想其他的办法。然后又想到LCA可以求两点的距离,那么可以考虑用LCA求两点距离,这里我们通过题意理解可以知道,如果选择一个深度最深的点作为其中一个点,那么其定会有一种与其他点的距离方案是我们的答案(简单的贪心),那么我们先确定一点的位置,然后再通过遍历和计算直接计算最大的距离不就OK了吗,然后就... AC了
代码如下
//#define fre yes #include <cstdio> #include <cstring> #include <iostream> const int N = 200005; int head[N << 1], to[N << 1], ver[N << 1]; int maxx[N], color[N], ans[N]; int depth[N], f[N][22], lg[N]; int n, m, root; int tot; void addedge(int x, int y) { ver[tot] = y; to[tot] = head[x]; head[x] = tot++; } void dfs(int u, int fa) { depth[u] = depth[fa] + 1; f[u][0] = fa; for (int i = 1; (1 << i) <= depth[u]; i++) { f[u][i] = f[f[u][i - 1]][i - 1]; } for (int i = head[u]; ~i; i = to[i]) { int v = ver[i]; if(v != fa) { dfs(v, u); } } } int LCA(int u, int v) { if(depth[u] < depth[v]) { std::swap(u, v); } while(depth[u] > depth[v]) { u = f[u][lg[depth[u] - depth[v]] - 1]; } if(u == v) return u; for (int i = lg[depth[u]] - 1; i >= 0; i--) { if(f[u][i] != f[v][i]) { u = f[u][i]; v = f[v][i]; } } return f[u][0]; } int Dis(int u, int v) { return depth[u] + depth[v] - 2 * depth[LCA(u, v)]; } int main() { memset(head, -1, sizeof(head)); scanf("%d %d", &n, &m); for (int i = 1; i <= n; i++) { int x, u; scanf("%d %d", &x, &u); color[i] = x; addedge(i, u); addedge(u, i); if(!u) root = i; } dfs(root, -1); for (int i = 1; i <= n; i++) { lg[i] = lg[i - 1] + (1 << lg[i - 1] == i); } for (int i = 1; i <= n; i++) { if(depth[maxx[color[i]]] < depth[i]) { maxx[color[i]] = i; } } for (int i = 1; i <= n; i++) { ans[color[i]] = std::max(ans[color[i]], Dis(maxx[color[i]], i)); } for (int i = 1; i <= m; i++) { printf("%d\n", ans[i]); } }