视频讲解:GESP202406 六级 二叉树-信息学奥赛GESP等级考试真题解析
一、原题
题目描述
小杨有一棵包含 n 个节点的二叉树,且根节点的编号为 1。这棵二叉树任意一个节点要么是白色,要么是黑色。之后小杨会对这棵二叉树进行 q 次操作,每次小杨会选择一个节点,将以这个节点为根的子树内所有节点的颜色反转,即黑色变成白色,白色变成黑色。
小杨想知道 q 次操作全部完成之后每个节点的颜色。
输入格式
第一行一个正整数 n,表示二叉树的节点数量。
第二行 (n−1) 个正整数,第 i(1≤i≤n−1)个数表示编号为 (i+1) 的节点的父亲节点编号,数据保证是一棵二叉树。
第三行一个长度为 n 的 01 串,从左到右第 i(1≤i≤n)位如果为 0,表示编号为 i 的节点颜色为白色,否则为黑色。
第四行一个正整数 q,表示操作次数。
接下来 q 行每行一个正整数 ai(1≤ai≤n),表示第 i 次操作选择的节点编号。
输出格式
输出一行一个长度为 n 的 01 串,表示 q 次操作全部完成之后每个节点的颜色。从左到右第 i(1≤i≤n) 位如果为 0,表示编号为 i 的节点颜色为白色,否则为黑色。
输入输出样例
输入 #1
6
3 1 1 3 4
100101
3
1
3
2
输出 #1
010000
说明/提示
样例解释
第一次操作后,节点颜色为:011010
第二次操作后,节点颜色为:000000
第三次操作后,节点颜色为:010000
数据范围
| 子任务编号 | 得分 | n | q | 特殊条件 |
|---|---|---|---|---|
| 1 | 20 | ≤10^5 | ≤10^5 | 对于所有 i≥2,节点 i 的父亲节点编号为 i−1 |
| 2 | 40 | ≤1000 | ≤1000 | |
| 3 | 40 | ≤10^5 | ≤10^5 |
对于全部数据,保证有 n,q≤105。
二、做题思路
1)填充数据

cpp
#include<bits/stdc++.h>
using namespace std;
map<int,vector<int>>g;
char ans[100005];
int c[100005];
int main(){
//1)填充数据
//1.1)确定结点数量n
int n;cin>>n;
//1.2)填充2至n的父节点
for(int i=2;i<=n;i++){
int t;cin>>t;
g[t].push_back(i);
}
//1.3)填充n个节点初始颜色
for(int i=1;i<=n;i++) cin>>ans[i];
//1.4)确定操作次数q
int q;cin>>q;
//1.5)确定q次操作的节点
while(q--){
int t;cin>>t;
c[t]++;
}
return 0;
}
2)深搜 计算当前节点操作次数 确定颜色
2.1-2)从根结点1开始搜 , 搜索每个节点

cpp
#include<bits/stdc++.h>
using namespace std;
map<int,vector<int>>g;
char ans[100005];
int c[100005];
//2.2)搜索每个节点
void dfs(int x,int now){
for(auto it:g[x]){
dfs(it,now);
}
}
int main(){
//1)填充数据
//...
//2)深搜 计算当前节点操作次数 确定颜色
//2.1)从根结点1开始搜
dfs(1,0);
return 0;
}
2.3)计算当前节点操作次数(受父节点影响,需继承之前的操作次数)

cpp
#include<bits/stdc++.h>
using namespace std;
map<int,vector<int>>g;
char ans[100005];
int c[100005];
//2.2)搜索每个节点
void dfs(int x,int now){
//2.3)计算当前节点操作次数
now+=c[x];
for(auto it:g[x]){
dfs(it,now);
}
}
int main(){
//1)填充数据
//...
//2)深搜 计算当前节点操作次数 确定颜色
//2.1)从根结点1开始搜
dfs(1,0);
return 0;
}
2.4)确定颜色(操作次数奇数,颜色取反,否则不变 )

cpp
#include<bits/stdc++.h>
using namespace std;
map<int,vector<int>>g;
char ans[100005];
int c[100005];
//2.2)搜索每个节点
void dfs(int x,int now){
//2.3)计算当前节点操作次数
now+=c[x];
//2.4)确定颜色
//操作次数奇数,颜色取反,否则不变
if(now%2==1) ans[x]=!(ans[x]-'0')+'0';
for(auto it:g[x]){
dfs(it,now);
}
}
int main(){
//1)填充数据
//...
//2)深搜 计算当前节点操作次数 确定颜色
//2.1)从根结点1开始搜
dfs(1,0);
return 0;
}
3)输出每个节点的颜色
cpp
#include<bits/stdc++.h>
using namespace std;
map<int,vector<int>>g;
char ans[100005];
int c[100005];
//2.2)搜索每个节点
void dfs(int x,int now){
//2.3)计算当前节点操作次数
now+=c[x];
//2.4)确定颜色
//操作次数奇数,颜色取反,否则不变
if(now%2==1) ans[x]=!(ans[x]-'0')+'0';
for(auto it:g[x]){
dfs(it,now);
}
}
int main(){
//1)填充数据
//...
//2)深搜 计算当前节点操作次数 确定颜色
//2.1)从根结点1开始搜
dfs(1,0);
//3)输出每个节点的颜色
for(int i=1;i<=n;i++) cout<<ans[i];
return 0;
}
三、答案
cpp
#include<bits/stdc++.h>
using namespace std;
map<int,vector<int>>g;
char ans[100005];
int c[100005];
//2.2)搜索每个节点
void dfs(int x,int now){
//2.3)计算当前节点操作次数
now+=c[x];
//2.4)确定颜色
//操作次数奇数,颜色取反,否则不变
if(now%2==1) ans[x]=!(ans[x]-'0')+'0';
for(auto it:g[x]){
dfs(it,now);
}
}
int main(){
//1)填充数据
//1.1)确定结点数量n
int n;cin>>n;
//1.2)填充2至n的父节点
for(int i=2;i<=n;i++){
int t;cin>>t;
g[t].push_back(i);
}
//1.3)填充n个节点初始颜色
for(int i=1;i<=n;i++) cin>>ans[i];
//1.4)确定操作次数q
int q;cin>>q;
//1.5)确定q次操作的节点
while(q--){
int t;cin>>t;
c[t]++;
}
//2)深搜 计算当前节点操作次数 确定颜色
//2.1)从根结点1开始搜
dfs(1,0);
//3)输出每个节点的颜色
for(int i=1;i<=n;i++) cout<<ans[i];
return 0;
}