题目大意
有n个点,如果其中一个点想成为独特根,那他每个路都不能有相同的值,有几个这样的独特根
题解
先任意设一个根,如果其中一个点a,他的子树中的点b有与a的值相同的值,那除了他的子树,其他的点为根,一定会有路经过a和b,所以除了他的子树,其他所有点都不行,反之,如果有非子树的点的值和a的值相同,那a的子树所有点也都不可取,为了快速标记,我们使用树上差分+dfs序,我的理解是后者为了方便最后的和值操作,而且标记子树也比较方便
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int MAX=2e5+5;
int total,n;
int a[MAX];
int in[MAX];
int out[MAX];
int cnt[MAX]; //每个数全局出现次数
int vis[MAX]; //每个数遍历时出现次数
int sum[MAX]; //记录差分数组
bool have[MAX]; //判断是否出现过
vector<int> value;
vector<int> edge[MAX];
void dfs(int index){
in[index]=++total;
int last=vis[a[index]]++; //last记录之前此权值的数量
for(int i=0;i<edge[index].size();i++){
int to=edge[index][i];
if(!have[to]){
have[to]=1;
int now=vis[a[index]];
dfs(to);
if(now<vis[a[index]]){
sum[1]++;
sum[n+1]--;
sum[in[to]]--;
sum[out[to]+1]++;
}
}
}
out[index]=total;
if(last||vis[a[index]]!=cnt[a[index]]){
sum[in[index]]++;
sum[out[index]+1]--;
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
value.push_back(a[i]);
}
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
edge[u].push_back(v);
edge[v].push_back(u);
}
sort(value.begin(),value.end());
value.erase(unique(value.begin(),value.end()),value.end());
for(int i=1;i<=n;i++){
a[i]=lower_bound(value.begin(),value.end(),a[i])-value.begin();
cnt[a[i]]++;
}
have[1]=1;
dfs(1);
int ans=0;
for(int i=1;i<=n;i++){
sum[i]+=sum[i-1];
if(!sum[i]){
ans++;
}
}
printf("%d",ans);
}