Luogu P2607 骑士

传送门

题意#

每个骑士有且只有一个讨厌的人,即他们两个不能同时出现在一个队伍之中.
每个骑士都有一个攻击力.求凑齐一个团队攻击力最大是多少.

分析#

因为每个骑士只有一个讨厌的人,所以可以建一个外向树(即每个节点只有一个父节点).
由于是 n个点n条边,因此一定存在环,我们只需要断一条环上的边,就可以得到一棵树
得到树之后,我们就可以愉快的进行树形DP了 ()

如何破环?#

我们首先需要找到环上的一个点,那么他和他的父节点所连边一定为环上一条边,我们考虑把它断开
首先我们知道,无论此边是否断开,这两个点是都不可以同时选的。
所以我们可以假设这条边被断开,分别以这条边的两个断点为根进行树形DP取最大值即可

AC_CODE#

#include <bits/stdc++.h>
#define fi first    
#define se second    
#define pb push_back
#define mk make_pair
#define leng(a) (int)a.size()
#define lowbit(x) (x&-x)
#define fix(a) fixed << setprecision(a) 
#define debug(x) cout<<#x" ----> "<<x<<endl
#define rep(i, b, s) for(int i = (b); i <= (s); ++i)
#define pre(i, b, s) for(int i = (b); i >= (s); --i)
#define TEST int T; read(T); while(T --) 

//#define int long long 
#define endl '\n' 
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
#define all(v) (v).begin(),(v).end()

using namespace std;
 
typedef unsigned long long ULL;
typedef pair<int, int> PII ;
typedef pair<int, PII> PIII ;// {value, {value, value}}
typedef pair<double, double> PDD ;
typedef long long LL;
const int INF = INT_MAX;
const LL INFF = INT64_MAX;
const int MOD = 998244353;
const double eps = 1e-10;
const double pi = acos(-1.0);
   
LL gcd(LL a, LL b) {return b ? gcd(b, a%b) : a;}
inline LL ksm(LL a, LL b) {if (b == 0) return 1; LL ns = ksm(a, b >> 1); ns = ns * ns % MOD; if (b & 1) ns = ns * a % MOD; return ns;}
inline LL lcm(LL a, LL b) {return a / gcd(a, b) * b;}
inline void out(bool flag);
template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }
template <typename T> inline T mod(T &x) {return x % MOD;}
template < typename T >
inline void read(T &x)
{
    x = 0; bool f = 0; char ch = getchar();
    while(!isdigit(ch)){f ^= !(ch ^ 45);ch=getchar();}
    while(isdigit(ch)) x= (x<<1)+(x<<3)+(ch&15),ch=getchar();
    x = f ? -x : x;
}


// __builtin_popcount(x) 返回x中1的个数
// __lg(x) 返回x的二进制下的位数(去除前导零实现,特判0)
//map<key,value>; key 可以是PII

//再用unordered_map 是sb
const int N = 1e6 + 10;
int h[N], e[N], ne[N], idx;
int w[N], fa[N];
int n, mark;
bool vis[N];
LL ans, res, RES;
LL f[N][2];
//加边
inline void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
//树形DP mark为标记过的环的端点
void dfs(int u) {
    f[u][1] = w[u], f[u][0] = 0;
    vis[u] = true;
    for(int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if(j != mark) {
            dfs(j);
            f[u][0] += max(f[j][0], f[j][1]);
            f[u][1] += f[j][0];
        } else f[j][1] = -INFF;
    }
}
//找环上的点  因为是外向树,每个点只有一个父节点
//我们一直对某个点寻找父节点 一定可以找到环上的点
void get_circle(int u) {
    vis[u] = true;
    while(!vis[fa[u]]) {
        u = fa[u];
        vis[u] = true;
    }
    res = 0;
    mark = u;
    dfs(mark);
    res = max(f[u][0], f[u][1]);
    mark = fa[mark];
    vis[mark] = true;
    dfs(mark);
    ans += max(max(f[mark][0], f[mark][1]), res);
}

inline void solve() {
    read(n); 
    rep(i, 1, n) h[i] = -1;
    rep(i, 1, n) {
        read(w[i]);
        read(fa[i]);
        add(fa[i], i);
        // cout << w[i] << endl;
    }    
    rep(i, 1, n)    
        if(!vis[i])
            get_circle(i);
    printf("%lld\n", ans);
}
 
signed main() 
{

    solve();
 
    return 0;
}
 
 
inline void out(bool flag) {
    if(flag) puts("YES");
    else puts("NO");
}
posted @   ccz9729  阅读(45)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
主题色彩