基环树

基环树

概念

基环树就是一棵树上多出一条边。也就是说,有 \(N\) 个点 \(N\) 条边的连通图叫做基环树。

对于有向图和无向图,分为有向基环树和无向基环树。有向基环树又分为内向基环树(每个节点以自己为起点的边只有一条)和外向基环树(每个节点以自己为终点的边只有一条)。

无向基环树

内向基环树

外向基环树

如果我们把环上的任意一条边断掉,那么这个图就会变为一棵树。如果把这个环全部断掉,这个图就会变为一个森林。

例题

[P2607 ZJOI2008] 骑士

首先,这个题非常类似于 没有上司的舞会。我们将每个骑士向他讨厌的人连边,这将会是一个基环树森林。对于每一棵基环树而言,如果他是一棵树,那么就和 没有上司的舞会 完全一样,一个简单的树形 dp。那么我们考虑断掉还上的一条边,分别以断边的两个端点为根跑树形 dp,那么这棵基环树的答案便是 \(\max(f_{u,0},f{v,0})\)

#include <bits/stdc++.h>
#define int long long 

using namespace std;

int read() {
	int x = 0; char ch = getchar();
	while (ch < '0' || ch > '9') ch = getchar();
	while (ch >= '0' && ch <= '9') {
		x = (x << 1) + (x << 3) + (ch ^ 48);
		ch = getchar();
	}
	return x;
}
const int N = 1e6 + 10;
int n, a[N], w[N];
struct edge {
	int y, id;
}; 
vector<edge> G[N];
int vis[N], inst[N], u, v, id;
void dfs(int x, int fr) {
	if (vis[x]) return;
	vis[x] = 1;
	inst[x] = 1;
	for (edge i : G[x]) {
		int y = i.y;
		if (i.id == fr) continue;
		if (inst[y]) {
			u = x, v = y, id = i.id;
			continue;
		}
		dfs(y, i.id);
	}
	inst[x] = 0;
}
int f[N][2];
void dp(int x, int fr) {
	f[x][1] = w[x];
	f[x][0] = 0; 
	for (edge i : G[x]) {
		int y = i.y;
		if (i.id == fr) continue;
		if (i.id == id) continue;
		dp(y, i.id);
		f[x][1] += f[y][0];
		f[x][0] += max(f[y][1], f[y][0]);
	}
}
signed main() {
	n = read();
	for (int i = 1; i <= n; i++) {
		w[i] = read(), a[i] = read();
		G[i].push_back({a[i], i});
		G[a[i]].push_back({i, i});
	}
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		if (vis[i]) continue;
		dfs(i, 0);
		dp(u, 0);
		int val = f[u][0];
		dp(v, 0);
		ans += max(val, f[v][0]);
	}
	printf("%lld\n", ans);
	
	return 0;
}

Others

这边有一个对于外向树用拓扑排序找环的小技巧:

for (int i = 1; i <= n; i++) {
    v[i] = read();
    G[v[i]].push_back(i);
    in[v[i]]++;
}
for (int i = 1; i <= n; i++) {
    if (in[i] == 0) q.push(i);
}
while (!q.empty()) {
    int y = q.front(); q.pop();
    tag[y] = 1;
    int x = v[y];
    // 此处可以进行一些其他的操作,比如转移
    in[x]--;
    if (in[x] == 0) q.push(x);
}
posted @ 2025-04-17 17:23  Zctf1088  阅读(22)  评论(0)    收藏  举报