Codeforces Round 928 (Div. 4) G. Vlad and Trouble at MIT 题解 树形dp

Vlad and Trouble at MIT

题目描述

弗拉迪斯拉夫有个儿子非常想去麻省理工学院。麻省理工学院(摩尔多瓦理工学院)的学生宿舍可以用一棵树来表示,树上有 n n n 个顶点,每个顶点代表一个房间,房间里正好有一个学生。树是一个连通的无向图,有 n n n 个顶点和 n − 1 n-1 n1 条边。

今晚,有三种类型的学生:

  • 想参加派对和玩音乐的学生(标记为 P \texttt{P} P );
  • 想睡觉和享受安静的学生(标记为 S \texttt{S} S );
  • 无所谓的学生(标记为 C \texttt{C} C )。

最初,所有的边缘都是薄墙,允许音乐通过,因此当参加派对的学生放音乐时,每个房间都能听到。但是,我们可以在任何边缘放置一些厚墙–厚墙不允许音乐通过。

学校希望安装一些厚墙,这样每个参加派对的学生都可以播放音乐,而睡觉的学生却听不到。
由于大学在冠名权诉讼中损失惨重,他们要求您找出他们需要使用的最少厚墙数量。

输入描述

第一行包含一个整数 t t t ( 1 ≤ t ≤ 1000 1 \leq t \leq 1000 1t1000 ) - 测试用例数。

每个测试用例的第一行包含一个整数 n n n ( 2 ≤ n ≤ 1 0 5 2 \leq n \leq 10^5 2n105 ) - 树中的顶点数。

每个测试用例的第二行包含 n − 1 n-1 n1 个整数 a 2 , … , a n a_2, \dots , a_n a2,,an ( 1 ≤ a i < i 1 \leq a_i < i 1ai<i ) - 这意味着树中的 i i i a i a_i ai 之间有一条边。

每个测试用例的第三行包含一个长度为 n n n 的字符串 s s s,由字符 P \texttt{P} P S \texttt{S} S C \texttt{C} C 组成,表示学生 i i i 的类型为 s i s_i si

所有测试用例中 n n n 的总和不超过 1 0 5 10^5 105

输出描述

对于每个测试用例,输出一个整数–所需的最小厚壁数。

样例输入

3
3
1 1
CSP
4
1 2 2
PCSS
4
1 2 2
PPSS

样例输出

1
1
2

原题

CF——传送门

代码

#include <bits/stdc++.h>
using namespace std;
#define max_Heap(x) priority_queue<x, vector<x>, less<x>>
#define min_Heap(x) priority_queue<x, vector<x>, greater<x>>
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<long long, long long> PLL;
const double PI = acos(-1);

#define INF 1e9 // 无穷大
const int maxn = 1e5 + 6;
int n;
string s;               // 节点类型
vector<int> edge[maxn]; // 存储从根节点指向子节点的有向边
int dp[maxn][3];        // dp[i][0]表示不接触水流,dp[i][1]表示只接触红色水流,dp[i][2]表示只接触蓝色水流

void dfs(int u)
{
    dp[u][0] = INF; // 初始化为无穷大,便于dp求三种情况最小值
    if (s[u] != 'S')
        dp[u][1] = 0;
    else
        dp[u][1] = INF; // 若该节点产生蓝色水流,则无法做到只接触红色水流
    if (s[u] != 'P')
        dp[u][2] = 0;
    else
        dp[u][2] = INF; // 若该节点产生红色水流,则无法做到只接触蓝色水流
    int tot = 0;
    for (int v : edge[u])
    {
        // 深搜,dp从叶子节点往根节点转移
        dfs(v);
        dp[u][1] = dp[u][1] + min({dp[v][1], dp[v][2] + 1, dp[v][0]}); // 只接触红色水流所需墙壁和,三种情况,其中只有子节点为蓝色水流需要加一个墙壁
        dp[u][2] = dp[u][2] + min({dp[v][2], dp[v][1] + 1, dp[v][0]}); // 只接触蓝色水流所需墙壁和,三种情况,其中只有子节点为红色水流需要加一个墙壁
        tot += dp[v][0];                                               // 无水流经过所需墙壁和
    }
    if (s[u] != 'C') // 如果不为'C',则该节点本身能产生水流,所以tot赋值无穷大,表示无法无水流
        tot = INF;
    dp[u][0] = min({tot, dp[u][1] + 1, dp[u][2] + 1}); // 该节点无水流的情况有三种:来者无水流,有水流+1个墙壁
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    // 本题策略:将'S'类比蓝色水流,将'P'类比红色水流,它们从叶子节点往根节点流,目的是设置最少墙壁使得蓝色水流和红色水流不交汇
    int t;
    cin >> t;
    while (t--)
    {
        cin >> n;
        // 记得清空之前测试中的边
        for (int i = 1; i <= n; i++)
        {
            edge[i].clear();
        }

        int v;
        for (int i = 2; i <= n; i++)
        {
            cin >> v;
            edge[v].push_back(i);
        }

        cin >> s;
        // 补充无关元素,使下标从1开始一一对应节点
        s = '0' + s;

        dfs(1);

        // 取三者最小值
        int ans = INT_MAX;
        for (int i = 0; i < 3; i++)
        {
            ans = min(ans, dp[1][i]);
        }
        cout << ans << '\n';
    }

    return 0;
}
  • 33
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值