给定一颗树且其结点有颜色,问是否存在一个节点以它为一个割点,分割出来的其他子树颜色相同。
有一丝启发式合并的韵味。首先很容易想到枚举每一个结点去判断它是否是满足条件的一个点。
但这样判断其分割得到的子树是否颜色相同时间复杂度会为O(n^2),首先考虑枚举一个点,求得的信息
和枚举其他点的信息是否存在重复,很容易想到去考虑一个结点和其根结点所要求的信息之间的关系,
如果把结点u作为割点,则必须得到1.子节点为根的子树颜色相同,父节点为根的子树颜色相同(且不包含u
结点的子树) 但如果把u节点的一个子节点v作为割点,则和上述的要求相同,这里可以看出这实际上是同类
型的问题,那么如果以u为根的子树不符合条件,且v为根的子树不符合条件,显然v的子树必定会影响到u,
那么,要使得u为根的子树符合条件,实际上就可能是去把v为根的子树中的某个结点作为割点,这显然是作为
一种启发,另外合并的操作在于合并u的信息和其他儿子的信息,具体的判断 不再阐述,看代码即可。
#include <iostream>
#include <queue>
#include <cstdio>
#include <cmath>
#include <vector>
#define MAX 100005
#define INF 0x3f3f3f3f
using namespace std;
int n,c[MAX],x,y,d[MAX],ans;
vector<int> G[MAX];
bool vis[MAX];
void dfs1(int v){//d[v]表示以v为根节点的子树的相同颜色,若有不同的其值为-1
vis[v] = true;
d[v] = c[v];
bool f = false;
for(int i = 0;i < G[v].size();++i){
int t = G[v][i];
if(!vis[t]){
dfs1(t);
f = true;
if(d[v] != d[t]) d[v] = -1;
}
}
vis[v] = false;
}
bool dfs2(int v,int pre){
vis[v] = true;
if(d[v] == -1){//1.存在子节点d[v] = -1 ! 2.存在两个子节点d[v] != d[u]
int counter = 0,u = -1;
for(int i = 0;i < G[v].size();++i){
int t = G[v][i];
if(vis[t]) continue;
if(d[t] == -1) counter++,u = t;
}
if(counter >= 2) return false;
//c <= 1
if(counter == 0) {ans = v;return true;}
else{ // c = 1
if(d[pre] != c[v]) return false;
for(int i = 0;i < G[v].size();++i){
int t = G[v][i];
if(vis[t] || t == u) continue;
if(d[t] != c[v]) return false;
}
d[v] = c[v];
return dfs2(u,v);
}
}
ans = v;
return true;
}
int main()
{
cin >> n;
for(int i = 1;i < n;++i){
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
for(int i = 1;i <= n;++i){
scanf("%d",&c[i]);
}
dfs1(1);
d[0] = c[1];
ans = -1;
if(dfs2(1,0)) cout << "YES" << endl << ans << endl;
else cout << "NO" << endl;
return 0;
}