大连网站制作建设网站制作多少钱
题目链接
题目大意
初始有一颗4个顶点的数, 1 1 1号点的度数为 3 3 3, 2 2 2 到 4 4 4 号点为叶子结点。 q q q ( 1 ≤ q ≤ 5 ∗ 1 0 5 ) (1 \leq q \leq 5*10^5) (1≤q≤5∗105)次操作,每次操作如下:
- 首先,选择树中编号为 v v v的叶子结点。
- 用 n n n表示此刻树上的结点数,然后向树上添加两个顶点,编号分别为 n + 1 n+1 n+1、 n + 2 n+2 n+2,同时得到新的边,一条在 v v v与 n + 1 n+1 n+1之间,另一条在 v v v与 n + 2 n+2 n+2之间。
每次询问输出当前树的直径。
思路
首先,树的直径一定是叶子结点到叶子结点。
设当前直径为 d 1 d_1 d1到 d 2 d_2 d2,若添加一个叶子结点 v v v,直径的变化有三种情况: ( 1 ) 、 (1)、 (1)、保持不变仍为 d 1 d_1 d1 到 d 2 d_2 d2 ; ( 2 ) 、 (2)、 (2)、变为 v v v 到 d 1 d_1 d1 ; ( 3 ) 、 (3)、 (3)、变为 v v v 到 d 2 d_2 d2 。只需要每次加入点都看看这三种情况,取一个最大的即可。
计算直径的大小可以借助 l c a lca lca来计算, u u u到 v v v的距离 d = d= d= d e e p t h ( u ) + d e e p t h ( v ) − 2 ∗ d e e p t h ( l c a ( u , v ) ) deepth(u)+deepth(v)-2*deepth(lca(u,v)) deepth(u)+deepth(v)−2∗deepth(lca(u,v)).
code
#include <bits/stdc++.h>
#define ll long long
#define pii pair<int, int>using namespace std;
const int N = 5e5 + 10, M = N * 2;
int deepth[M], anc[M][21];
vector<int> a[M];
int d[2] = {2, 3}, t[2];void dfs(int x, int fa)
{deepth[x] = deepth[fa] + 1;anc[x][0] = fa;for (int i = 1; i < 21; ++i)anc[x][i] = anc[anc[x][i - 1]][i - 1];for (auto k : a[x]){if (k == fa)continue;dfs(k, x);}
}int lca(int a, int b)
{if (deepth[a] < deepth[b])swap(a, b);for (int i = 20; i >= 0; --i)if (deepth[anc[a][i]] >= deepth[b])a = anc[a][i];if (a == b)return b;for (int i = 20; i >= 0; --i){if (anc[a][i] != anc[b][i]){a = anc[a][i];b = anc[b][i];}}return anc[a][0];
}int main()
{ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);int idx = 2;for (int i = 0; i < 3; ++i){a[1].push_back(idx);a[idx++].push_back(1);}dfs(1, 0);int len = 2;int q;cin >> q;for (int i = 0; i < q; ++i){int x;cin >> x;t[0] = idx, t[1] = idx + 1;a[x].push_back(idx);a[idx].push_back(x);dfs(idx, x);idx++;a[x].push_back(idx);a[idx].push_back(x);dfs(idx, x);idx++;for (int j = 0; j < 2; ++j)for (int k = 0; k < 2; ++k){int tmp1 = lca(t[j], d[k]);int tmp2 = deepth[t[j]] + deepth[d[k]] - deepth[tmp1] * 2;if (tmp2 > len){len = tmp2;d[1 - k] = t[j];}}cout << len << '\n';}return 0;
}