Codeforces Round #551 (Div. 2)D. Serval and Rooted Tree (树形dp 思维)

题目链接

题意:

给你一颗以 1 为根的树,树的形状确定,每个节点有一个类型 0 或 1,0 代表该点的权值取其儿子中权值最小的那一个,1 则反之。所有的叶子节点权值为 1 - m(m为叶子节点数量),但是并不知道每个叶子节点的具体权值。最后要求出根(即节点1)的权值最大可能是多少。

题解:

由于叶子节点的权值是不确定的,所以按照普通的递推是行不通的,那么我们可以按照 该节点最大可以排第几 来递推,也就是 dp[i] 表示 i 节点在 1 - m 中可以排第 dp[i] 位。那么可以得出,如果该点的类型为 1 ,则 dp[i] 就等于 i 点的儿子中大小排名最靠前的那个,也就是 dp[son] 最小的那个;若该点类型为 0 ,则 dp[i] 就等于 i 点的所有儿子的 dp 之和(由于叶子节点的权值都不相同,所以排名是所有的儿子累加。例如:假设一个点只有两个儿子,左儿子最前能排在第三位,右儿子最前能排在第四位,由于该点的权值必须取儿子中最小的那个,所以排名最前只能排在 3 + 4 = 7 位)。

代码:

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <bitset>
using namespace std;

typedef long long ll;
#define int ll
#define INF 0x3f3f3f3f3f3f3f3f
#define MAXM 200000 + 10
#define MAXN 300000 + 10
const ll mod = 1e9 + 7;
#define P pair<int, int>
#define fir first
#define sec second

int n;
int kd[MAXN], num;
vector<int> G[MAXN];
int dp[MAXN];

void dfs(int now)
{
    if(!G[now].size()) {
        dp[now] = 1;
        num ++;
        return ;
    }

    int temp = kd[now] ? INF : 0;
    for(int i = 0; i < G[now].size(); i ++) {
        dfs(G[now][i]);
        temp = kd[now] ? min(temp, dp[G[now][i]]) : temp + dp[G[now][i]];
    }
    dp[now] = temp;
}

signed main()
{
    cin >> n;
    for(int i = 1; i <= n; i ++) cin >> kd[i];
    for(int i = 2; i <= n; i ++) {
        int fa; cin >> fa;
        G[fa].push_back(i);
    }

    dfs(1);
    
    cout << num - dp[1] + 1 << endl;
}

/*

The WAM is F**KING interesting .

*/

猜你喜欢

转载自blog.csdn.net/ooB0Boo/article/details/89349813