← 全部文章

CF516D Drazil and Morning Exercise 题解

本文目录(3 节)
  1. 题意
  2. 题解
  3. 代码

CF516D Drazil and Morning Exercise

题意

  • 给定一棵 nn 个点的树,边有边权。
  • 定义 fx=max⁡i=1ndist(x,i)f_x = \max_{i=1}^n \text{dist}(x,i)。
  • qq 次询问最大的满足 max⁡x∈sfx−min⁡x∈sfx≤l\max_{x \in s} f_x - \min_{x \in s} f_x \le l 的连通块 ss 包含的点数。
  • n≤105n \le 10^5,q≤50q \le 50。

题解

先把 ff 线性求出来,然后由大到小双指针扫一遍,发现只会合并,因此并查集维护连通性即可,时间复杂度 O(nlog⁡n+qnα(n))\mathcal O(n \log n + qn\alpha(n))。

代码

const int N = 1e5 + 7;
int n, q, fa[N], s[N], v[N], ans;
ll m, f[N][2], g[N];
pair< ll, int > p[N];
vector< pi > e[N];

void dfs1(int x, int fa) {
	for (ui i = 0; i < e[x].size(); i++) {
		int y = e[x][i].fi;
		if (y == fa) continue;
		dfs1(y, x);
		ll z = f[y][0] + e[x][i].se;
		if (z > f[x][1]) f[x][1] = z;
		if (z > f[x][0]) swap(f[x][0], f[x][1]);
	}
}

void dfs2(int x, int fa) {
	for (ui i = 0; i < e[x].size(); i++) {
		int y = e[x][i].fi;
		if (y == fa) continue;
		int z = e[x][i].se;
		g[y] = max(g[x], f[y][0] + z == f[x][0] ? f[x][1] : f[x][0]) + z;
		dfs2(y, x);
	} 
}

int get(int x) {
	return x == fa[x] ? x : (fa[x] = get(fa[x]));
}

inline void merge(int x, int y) {
	if (x == y) return;
	if (s[x] > s[y]) swap(x, y);
	fa[x] = y, s[y] += s[x];
	ans = max(ans, s[y]);
}

int main() {
	rd(n);
	for (int i = 1, x, y, z; i < n; i++)
		rd(x), rd(y), rd(z), e[x].pb(mp(y, z)), e[y].pb(mp(x, z));
	dfs1(1, 0);
	dfs2(1, 0);
	for (int i = 1; i <= n; i++) p[i] = mp(max(f[i][0], g[i]), i);
	sort(p + 1, p + n + 1);
	reverse(p + 1, p + n + 1);
	rd(q);
	while (q--) {
		rd(m), ans = 1;
		for (int i = 1; i <= n; i++) fa[i] = i, s[i] = 1, v[i] = 0;
		for (int l = 1, r = 0; l <= n; l++) {
			while (r < n && p[l].fi - p[r+1].fi <= m) {
				int x = p[++r].se;
				v[x] = 1;
				for (ui i = 0; i < e[x].size(); i++) {
					int y = e[x][i].fi;
					if (v[y]) merge(get(x), get(y));
				}
			}
			--s[get(p[l].se)], v[get(p[l].se)] = 0;
		}
		print(ans);
	}
	return 0;
}

标签:DP、Two Pointers、并查集、换根 DP、树形 DP

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…