题目链接:
http://codeforces.com/contest/379/problem/F
题目大意:
给定一棵树,
每次向树上的某个节点添加两个子节点,然后询问树的直径(树上最长路径)。
算法:
如果我们记录下添加节点前树上的最远路径长度及两个端点A,B。
那么添加两个新节点之后,距离这个点最远的那个点,肯定是原直径的两个端点之一。
所以只要计算dis(新节点1,A)和dis(新节点2,B),如果比原答案大的话,就更新答案和A、B的值。
于是就变成了一道LCA模板题,求LCA的时候顺便求出两点距离也是很简单的事情 。
PS:
谢谢wuyiqi大神和sjb大神给我讲了一些这道题需要的性质
推荐一篇关于树的直径的文章
代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> const int MAXN = 1100000; const int MAXD = 20; int go[MAXN][MAXD], dep[MAXN]; std::vector <int> mm[MAXN]; void dfs(int u) { for(int i = 0; i < mm[u].size(); i ++) { if (dep[mm[u][i]] != -1) { continue; } int v = mm[u][i]; dep[v] = dep[u] + 1; go[v][0] = u; for (int k = 1; (1 << k) <= dep[v]; k ++) { go[v][k] = go[go[v][k - 1]][k - 1]; } dfs(v); } } int lca(int u, int v) { if (dep[v] > dep[u]) { std::swap(u,v); } int jmp = dep[u] - dep[v]; for (int k = MAXD - 1; k >= 0; k --) { if(jmp >> k & 1) { u = go[u][k]; } } if (u == v) { return jmp; } int tmp = 0; for(int k = MAXD - 1; k >= 0; k --) { if(((1 << k) < dep[u]) && (go[u][k] != go[v][k])) { u = go[u][k]; v = go[v][k]; tmp |= 1 << k; } } tmp ++; return jmp + tmp * 2; } int main() { int n; scanf("%d", &n); for(int i = 0; i < 2 * n + 4; i ++) { mm[i].clear(); } memset(dep, -1, sizeof(dep)); memset(go, -1, sizeof(go)); mm[0].push_back(1); mm[0].push_back(2); mm[0].push_back(3); dep[0] = 0; dfs(0); int node1 = 1, node2 = 2; int ans = 2; for (int i = 0; i < n; i ++) { int x; scanf("%d",&x); x --; mm[x].push_back(i * 2 + 4); mm[x].push_back(i * 2 + 5); dfs(x); int tmp; if ((tmp = lca(node1, i * 2 + 4)) > ans) { node2 = i * 2 + 4; ans = tmp; } if ((tmp = lca(node2, i * 2 + 5)) > ans) { node1 = i * 2 + 5; ans = tmp; } printf("%d\n", ans); } return 0; }