電力

maniubi發表於2024-09-01

電力

題意

求一個圖刪除一個點之後,聯通塊最多有多少。

思路

先計算出原來有多少個聯通塊,再計算每個點對聯通塊的貢獻的最大值。

考慮跑一遍 tarjan,孤立點的貢獻為 \(-1\),非割點貢獻為 \(0\),割點貢獻為 dfs 樹上 \(low_v \ge dfn_u\)\(v\) 的個數,根的貢獻為 dfs 樹上的兒子個數減一。

程式碼

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5 + 5;
int n, m, cnt, ans, d, dfn[N], low[N];
int tot, ver[N << 1], nxt[N << 1], head[N];
void add(int x, int y) {
	ver[++ tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
}
void tarjan(int x, int fa) {
	low[x] = dfn[x] = ++ cnt;
	int son = 0, CNT = 0;
	for (int i = head[x], y; i; i = nxt[i]) {
		y = ver[i];
		if (!dfn[y]) {
			tarjan(y, x); son ++;
			low[x] = min(low[x], low[y]);
			CNT += (low[y] >= dfn[x]); 
		} else if (y != fa)
			low[x] = min(low[x], dfn[y]);
	}
	if (!fa && !son) d = max(d, -1); // 孤立點
	if (!fa && son == 1) d = max(d, 0); // 非割點
	if (fa) d = max(d, CNT); // 割點
	if (!fa && son >= 2) d = max(d, son - 1); // 根
}
int solve() {
	cin >> n >> m;
	if (!n && !m) return 1;
	memset(head, 0, sizeof(head));
	memset(dfn, 0, sizeof(dfn));
	memset(low, 0, sizeof(low));
	tot = 0, cnt = 0, ans = 0, d = -1e9;
	for (int i = 1, u, v; i <= m; i ++) {
		cin >> u >> v, u ++, v ++;
		add(u, v); add(v, u);
	}
	for (int i = 1; i <= n; i ++) 
		if (!dfn[i]) {
			ans ++;
			tarjan(i, 0);
		}
	if (d == -1e9) d = 0;
	cout << ans + d << "\n";
	return 0;
}
int main() {
	while (1) 
		if (solve()) 
			break;
	return 0;
}

相關文章