题目链接,D题
题意: 给出一颗树,树上每条边都有一个颜色,定义如果以这个点为端点的所有简单路径都没有相邻的两条边颜色相同时,这个点为good点,求
g
o
o
d
good
good点的数量以及是哪个点。
思路:
假设节点u和节点v均与节点w相邻,且e(w,u)与e(w,v)颜色相同,那么要分两种情况:
- u u u, v v v均是w的子树,则这两个点的子树上所有的点包括这两个点都不是 g o o d good good点。
-
u
u
u是
w
w
w的父节点,
v
v
v是
w
w
w的子节点,则u的外子树和u的内子树除
w
w
w拿部分外的节点都不是
g
o
o
d
good
good点。如下图:
于是可以用 d f s dfs dfs序来做, d f s dfs dfs序感觉考的很少,但在这道题里有大用, [ s t [ u ] , e n [ u ] ] [st[u],en[u]] [st[u],en[u]]的所有点就构成了u的子树,可以用差分的思想,给 s t [ u ] , e n [ u ] st[u],en[u] st[u],en[u]打上标记,最后再求前缀和。
#include<bits/stdc++.h>
#define ll unsigned long long
using namespace std;
const int N = 1e5 + 10;
int n, h[N], cnt, st[N], en[N], dfn, pre[N], s[N], ans;
pair<int, int> p[N];
struct node {
int v, w, nt;
} no[N];
void add(int u, int v, int w) {
no[cnt] = node{v, w, h[u]};
h[u] = cnt++;
}
void dfs(int u, int fa) {
st[u] = ++dfn;
pre[u] = fa;
for(int i = h[u]; ~i; i = no[i].nt) {
int v = no[i].v;
if(v != fa)
dfs(v, u);
}
en[u] = dfn;
}
int main() {
memset(h, -1, sizeof h);
scanf("%d", &n);
for(int u, v, w, i = 1; i < n; i++) {
scanf("%d%d%d", &u, &v, &w);
add(u, v, w), add(v, u, w);
}
dfs(1, 0);
for(int i = 1; i <= n; i++) {
int num = 0, k;
for(int j = h[i]; ~j; j = no[j].nt)
p[num++] = make_pair(no[j].w, no[j].v);
sort(p, p + num);
for(int j = 0; j < num; j = k) {
for(k = j; p[k].first == p[j].first && k < num; k++);
if(k > j + 1)
for(int x = j; x < k; x++) {
int v = p[x].second;
if(v == pre[i])//第一种情况
s[1]++, s[st[i]]--, s[en[i] + 1]++;
else//第二种情况
s[st[v]]++, s[en[v] + 1]--;
}
}
}
for(int i = 1; i <= n; i++)
s[i] += s[i - 1];
for(int i = 1; i <= n; i++)
if(!s[st[i]])
ans++;
printf("%d\n", ans);
for(int i = 1; i <= n; i++)
if(!s[st[i]])
printf("%d\n", i);
return 0;
}