2025.03.19 CW 模拟赛 A. 前端

题面 & 题解

A. 前端

题意

你是一个前端程序员。有一天同事来问你这个问题:

有一张 \(n\) 个点 \(m\) 条边的简单无向图, 每个点有一个正整数的权值. 现在有人打算按一个顺序依次删除这 \(n\) 个点.

定义一个连通块的权值为连通块内所有点的权值的和. 他想要知道, 每次删除了一个点之后, 图中所有连通块权值的最大值. 如果图中已经不存在连通块了, 则输出 0.

思路

正难则反, 我们倒着考虑.

这样, 我们的操作就变成

  • 加入一个点
  • 将该点和其的出边中已经出现过的点连起来.

这样, 我们使用并查集维护当前的连通块, 由于点权是正数, 所以权值一定会越来越大, 每次 \(\rm{merge}\) 操作的时候取 \(\max\) 即可.

#include <iostream>
#include <vector>

using namespace std;

#define int long long

constexpr int N = 1e5 + 5;

int n, m, fa[N], p[N], w[N], ans[N], mx;
bool vis[N];
vector<int> e[N];

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

void merge(int x, int y) {
	x = find(x), y = find(y);
	if (x ^ y) {
		w[x] += w[y];
		mx = max(mx, w[x]);
		fa[y] = x;
	}
}

void init() {
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
		cin >> w[i];
		fa[i] = i;
	}
	for (int i = 1, u, v; i <= m; ++i) {
		cin >> u >> v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	for (int i = n; i; --i) {
		cin >> p[i];
	}
}

void calculate() {
	for (int i = 1; i <= n; ++i) {
		ans[i] = mx;
		for (int j : e[p[i]]) {
			if (!vis[j]) {
				continue;
			}
			merge(p[i], j);
		}
		mx = max(mx, w[find(p[i])]);
		vis[p[i]] = true;
	}
	for (int i = n; i; --i) {
		cout << ans[i] << '\n';
	}
}

void solve() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr), cout.tie(nullptr);
	init();
	calculate();
}

signed main() {
	solve();
	return 0;
}
posted @ 2025-03-19 19:39  Steven1013  阅读(7)  评论(0)    收藏  举报