思路
颜色平衡树,即子树中的节点颜色均匀分布。所以要确认一个子树是否为颜色平衡树,需要得到它的所有节点的颜色,也就是要深搜它所有的子树。
这个想法就很标准的启发式合并了,何为启发式合并?简单来说,就是需要对每一个树进行搜索,在搜索的过程中必然会产生重复搜索的情况。如何减少重复搜索呢,那就是提前对每一个树(父节点树)的最大的那个子树(最大即节点最多)进行记录。提前是说提前到搜索这个最大的子树时,当搜索完它的最大子树时我们不删除这个子树上的信息,在搜索父结点树的时候就可以直接用了,不需要再去重新搜索了。
很板子了,思路不明白的再去看看其他博客。
代码
#include <bits/stdc++.h>
#define N 200010
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;int n,color[N],ans;
vector<int> ve[N];
unordered_map<int,int> cnt;int nums[N],son[N],nowson;
void dfs(int u,int fa){//求重儿子 nums[u]=1;son[u]=0;for(auto &v:ve[u]){if(v==fa) continue;dfs(v,u);nums[u]+=nums[v];if(nums[son[u]]<nums[v]) son[u]=v;}
}void calc(int u,int fa,int c){//计算 cnt[color[u]]+=c;if(cnt[color[u]]==0) cnt.erase(color[u]); for(auto &v:ve[u]){if(v==fa||v==nowson) continue;calc(v,u,c);}
}void answer(){int fg=1,pre=0;for(auto it:cnt){if(pre&&it.second!=pre) fg=0;pre=it.second;}ans+=fg;
}void dsu(int u,int fa,int keep){//dfs深搜 for(auto &v:ve[u]){if(v==fa||v==son[u]) continue;//重儿子不搜 dsu(v,u,0);}if(son[u]){//若有重儿子,单独搜 dsu(son[u],u,1);nowson=son[u];} calc(u,fa,1);//计算当前节点 nowson=0;answer();//判断是否颜色平衡 if(!keep) calc(u,fa,-1);//非重儿子删除记录
}int main(){IOScin>>n;for(int i=1;i<=n;i++){int f;cin>>color[i]>>f;if(f) ve[i].push_back(f),ve[f].push_back(i);}dfs(1,-1);dsu(1,-1,0);cout<<ans<<endl;return 0;
}