Codeforces Round #603 (Div. 2) F. Economic Difficulties DFS+DP

Codeforces Round #603 Div. 2 F. Economic Difficulties DFS+DP


传送门: https://codeforces.ml/contest/1263/problem/F

题意

给 两 棵 树 , 一 颗 在 上 面 , 另 一 颗 在 下 面 , 两 棵 树 的 叶 子 节 点 都 连 接 着 一 台 机 器 。 给两棵树,一颗在上面,另一颗在下面,两棵树的叶子节点都连接着一台机器。

问 , 最 多 删 掉 多 少 条 边 , 每 个 机 器 都 至 少 存 在 一 条 路 径 到 达 两 个 根 节 点 1 中 的 一 个 。 问,最多删掉多少条边,每个机器都至少存在一条路径到达两个根节点1中的一个。 1

思路

考 虑 d p , 设 d p [ i ] 表 示 前 i 个 机 器 通 电 的 最 大 删 除 边 数 。 考虑dp,设dp[i]表示前i个机器通电的最大删除边数。 dpdp[i]i
设 v a l [ 0 / 1 ] [ L ] [ R ] 表 示 上 / 下 树 中 区 间 [ L , R ] 最 大 删 除 边 数 。 设val[0/1][L][R]表示上/下树中区间[L,R]最大删除边数。 val[0/1][L][R]/[L,R]
即 转 移 方 程 为 : 即转移方程为:
d p [ i ] = m a x ( d p [ i ] , d p [ j ] + m a x ( v a l [ 0 ] [ j + 1 ] [ i ] , v a l [ 1 ] [ j + 1 ] [ i ] ) ) dp[i]=max(dp[i],dp[j]+max(val[0][j+1][i],val[1][j+1][i])) dp[i]=max(dp[i],dp[j]+max(val[0][j+1][i],val[1][j+1][i]))

所 以 要 怎 么 处 理 这 个 v a l 呢 ? 所以要怎么处理这个val呢? val

在 树 上 处 理 , 第 一 想 到 的 就 是 d f s ! 在树上处理,第一想到的就是dfs! dfs

因 为 叶 子 节 点 都 是 与 机 器 相 连 的 , 所 以 n 个 叶 子 节 点 类 似 于 线 段 的 端 点 。 因为叶子节点都是与机器相连的,所以n个叶子节点类似于线段的端点。 n线
对 于 每 个 子 树 , 都 会 有 一 个 端 点 区 间 , 当 删 除 该 子 树 时 , 该 区 间 内 的 机 器 无 法 到 达 根 节 点 。 对于每个子树,都会有一个端点区间,当删除该子树时,该区间内的机器无法到达根节点。
删 除 该 子 树 后 , 贡 献 为 子 树 大 小 即 s i z e [ i ] 条 边 。 删除该子树后,贡献为子树大小即size[i]条边。 size[i]

d f s 过 程 中 不 断 更 新 v a l [ L ] [ R ] 即 可 。 dfs过程中不断更新val[L][R]即可。 dfsval[L][R]

Code

#include "bits/stdc++.h"
using namespace std;

#define endl "\n"
#define eb emplace_back
#define mem(a, b) memset(a , b , sizeof(a))

const int N = 2e3 + 10;
int val[2][N][N];
int l[2][N], r[2][N];
vector<int> g[2][N];
int siz[N];
int dp[N];

void dfs(int u, int fa, int opt) {
    if(u != 1) siz[u] = 1;
    for(auto v : g[opt][u]) {
        if(v == fa) continue;
        dfs(v, u, opt);
        siz[u] += siz[v];
        l[opt][u] = min(l[opt][u], l[opt][v]);
        r[opt][u] = max(r[opt][u], r[opt][v]);
    }
    val[opt][l[opt][u]][r[opt][u]] = max(val[opt][l[opt][u]][r[opt][u]], siz[u]);
}

void solve() {
    int n; cin >> n;
    mem(l, INF);
    for(int i = 0;i < 2; i++) {
        siz[1] = 0;
        int a; cin >> a;
        for(int j = 2;j <= a; j++) {
            int u; cin >> u;
            g[i][u].eb(j);
            g[i][j].eb(u);
        }
        for(int j = 1;j <= n; j++) {
            int v; cin >> v;
            l[i][v] = j;
            r[i][v] = j;
        }
        dfs(1, 0, i);
    }
    for(int i = 1;i <= n; i++) {
        for(int j = 0;j < i; j++) {
            dp[i] = max(dp[i], dp[j] + max(val[0][j + 1][i], val[1][j + 1][i]));
        }
    }
    cout << dp[n] << endl;
}

signed main() {
    solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值