基环树

题目:骑士

题目大意:

给出n个骑士,每个骑士有且仅有一个厌恶的骑士(用边相连),我们需要选出一些骑士,条件是,骑士团中不能存在他们讨厌的骑士。

思路:

将这种关系抽象成图:一个n个点,n个边的图(每个点有对应的一个边),图中存在环。考虑用基环树解题。
算法步骤:

1、对于每个点:1~n
2、若之前访问过这个点,不进行再次访问。
   否则,以这个点为根进行深度优先搜索,找出一个环,并找出两个顶点,使得这两个顶点连接的边断开之后,
   能形成以这两个顶点为根的两棵树。
3、对这两棵树进行树形dp,求出这两棵树的dp最大值。然后取max,值加入res
4、输出res,得到最终结果。

关键:用st状态访问数组,判断每个点是否访问过,访问过的点不再访问,避免因为环造成死循环,同时避免同一个点的值多次相加
树形dp的过程参考:没有上司的舞会

#include <bits/stdc++.h>
using i64 = long long;
const int N = 2e6 + 10;
i64 n;
std::vector<i64> g[N], w(N);
std::vector<std::pair<i64, i64>> dp(N);
bool st[N];
void find(int u, int root, int &r1, int &r2) {
    st[u] = true;
    // 遍历每个儿子
    for (auto e : g[u]) {
        if (e == root) {
            r1 = u, r2 = e;
            return;
        }
        if (st[e]) continue;
        find(e, root, r1, r2);
    }
}
i64 dfs(int u, int root) {
    dp[u].first = 0, dp[u].second = w[u];
    if (g[u].size() == 0) return 0;
    for (auto e : g[u]) {
        if (e == root) continue; // 避免重复计算,保证当前的数据结构是一棵树
        dfs(e, root);
        dp[u].first += std::max(dp[e].first, dp[e].second);
        dp[u].second += dp[e].first;
    }
    return dp[u].first; // 返回dp[u][0],保证当前节点不选,这样才能保证环上两个节点不能同时被选,而不选这个点的话,最优情况就是对这两个端点的树形dp取最大值就行
}
void solve() {
	// register 用于将局部变量存储到寄存器中,提高访问速度
    std::cin >> n;
    for (int i = 1; i <= n; i ++) {
        int v;
        std::cin >> w[i] >> v;
        g[v].push_back(i);
    }
    i64 res = 0;
    for (int i = 1; i <= n; i ++) {
        if (!st[i]) {
            // 保证当前点没有访问过
            int r1 = 0, r2 = 0;
            // 深搜,找环的两个顶点
            find(i, i, r1, r2);
            if (r1) {
                // 找到环了,以这两个点为根,树形dp找最大值
                i64 ans1 = dfs(r1,r1); // 传入的参数为根节点,和初始节点,保证搜索的是一棵树,避免陷入死循环
                i64 ans2 = dfs(r2, r2);
                res += std::max(ans1, ans2);
            }
        }
    }
    std::cout << res << "\n";
}
int main()
{	
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	int t = 1; 
	// std::cin >> t;
	while (t --) {
		solve();
	}
	return 0;
}
posted @ 2025-05-15 13:06  来杯whiskey  阅读(29)  评论(0)    收藏  举报