G. Vlad and Trouble at MIT
题意
给定一颗 n n n 个节点的树,每个节点有一个学生,学生有三种类型:
- 参加派对的 P P P 类型,会制造噪音
- 想要睡觉的 S S S 类型,不希望被吵到
- 有没有噪音都可以的 C C C 类型
噪音会沿着树上的边不停传播,除非在某些边建一堵墙,现在给定每个节点的学生类型 s i s_i si
求出最少建多少堵墙可以满足 S S S 类型学生不被打扰
思路
我们可以想象一下从 P P P 类型的学生节点会流出蓝色的水流,而 S S S 类型的学生节点会流出红色的水流,我们要求蓝色和红色的水流不能相交,通过建墙后,可能有些节点是没有水流的。那么我们可以发现每个节点只有三种状态:蓝色水流、红色水流、没有水流。
我们对于当前的节点 u u u 和它的子节点 v v v,可以发现如果 u u u 是蓝色的,那么 v v v 必须是蓝色或者没有水流,否则如果 v v v 是红色,就必须建一堵墙来隔离。如果 u u u 没有水流,那么 v v v 必须没有水流,或则建墙隔离。
这里我们就可以发现是存在子状态的,我们可以用 d p [ u ] [ 0 ] , d p [ u ] [ 1 ] , d p [ u ] [ 2 ] dp[u][0],dp[u][1],dp[u][2] dp[u][0],dp[u][1],dp[u][2] 分别表示节点 u u u 没有水流、蓝色、红色需要建的最少的墙的数量,那么最终的答案就是: m i n ( d p [ 1 ] [ 0 ] , d p [ 1 ] [ 1 ] , d p [ 1 ] [ 2 ] ) min(dp[1][0],dp[1][1],dp[1][2]) min(dp[1][0],dp[1][1],dp[1][2])
注意如果一个节点是 C C C 类型,它要变成蓝色,它的子节点不是一定要选择至少一个蓝色,也可以全都无色或者被隔离,这样子虽然这个节点是无色,我们将数值存储在蓝色里( d p [ u ] 0 ] = d p [ u ] [ 1 ] ] dp[u]0] = dp[u][1]] dp[u]0]=dp[u][1]]),这里不会影响后续的节点处理,因为把无色当成蓝色不会影响其他的转移(多建一堵墙不如直接选择 d p [ u ] [ 0 ] dp[u][0] dp[u][0])
时间复杂度: O ( n ) O(n) O(n)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;
const int INF=0x3f3f3f3f;
const long long INFLL=1e18;
typedef long long ll;
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int t;
std::cin >> t;
while(t--){
int n;
std::cin >> n;
std::vector<std::vector<int>> g(n + 1, std::vector<int>());
std::string s;
fore(i, 2, n + 1){
int fa;
std::cin >> fa;
g[fa].push_back(i);
}
std::cin >> s;
s = '0' + s;
std::vector<std::vector<int>> dp(n + 1, std::vector<int>(3, INF));
auto dfs = [&](auto self, int u) -> void {
if(s[u] == 'P') dp[u][1] = 0;
else if(s[u] == 'S') dp[u][2] = 0;
else dp[u][0] = dp[u][1] = dp[u][2] = 0;
for(auto v : g[u]){
self(self, v);
if(s[u] == 'P') dp[u][1] += std::min({dp[v][1], dp[v][0], dp[v][2] + 1});
else if(s[u] == 'S') dp[u][2] += std::min({dp[v][2], dp[v][0], dp[v][1] + 1});
else{
dp[u][0] += std::min({dp[v][0], dp[v][1] + 1, dp[v][2] + 1});
dp[u][1] += std::min({dp[v][1], dp[v][2] + 1, dp[v][0]});
dp[u][2] += std::min({dp[v][2], dp[v][1] + 1, dp[v][0]});
}
}
};
dfs(dfs, 1);
std::cout << std::min({dp[1][0], dp[1][1], dp[1][2]}) << endl;
}
return 0;
}