题意:有一棵具有 n n n 个节点的树,每个节点代表一个集合,表示集合内两两元素的颜色不同,同时保证集合中拥有某个元素的所有的节点附带着边提取出来,能够形成一个连通图,即不可能出现有三个节点 v 1 , v 2 , v 3 v1,v2,v3 v1,v2,v3 , v 1 v1 v1 与 v 2 v2 v2 有树边, v 2 v2 v2 和 v 3 v3 v3 有树边,而 v 1 , v 3 v1,v3 v1,v3 集合中有元素 x x x ,而 v 2 v2 v2 集合中没有。
思路: d f s dfs dfs 遍历一遍整棵树,在遍历的过程染色。由于单一元素所处的集合都相邻,那么我们可以贪心得直接给一个元素染上未出现在集合中得颜色。
ps:每次把已经染色的节点加入栈中,标记其颜色,染完集合中所有元素后,按栈弹出,取消颜色的标记。
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<vector>
using namespace std;
#define NUM 300005
int n, m;
int num[NUM] = {};
vector<int> s[NUM];
vector<int> d[NUM];
int ans = 0, color[NUM] = {};
int head[NUM] = {}, tot = 0;
int sta1[NUM], top1 = 0, sta2[NUM], top2 = 0;
bool check[NUM] = {};
struct edge
{
int to, next;
} e[NUM << 1];
void dfs(int v, int fa)
{
for (int i = 0; i < num[v]; ++i)
{
if (color[s[v][i]])
{
check[color[sta2[++top2] = s[v][i]]] = true;
continue;
}
sta1[++top1] = s[v][i];
}
int c = 1;
while (top1)
{
while(check[c])
++c;
color[sta1[top1--]] = c++;
}
while (top2)
check[color[sta2[top2--]]] = false;
for (int i = head[v]; i != 0; i = e[i].next)
{
if (e[i].to == fa)
continue;
dfs(e[i].to, v);
}
}
inline void AC()
{
cin >> n >> m;
int x, y;
for (int i = 1; i <= n; ++i)
{
scanf("%d", num + i);
if (num[ans] < num[i])
ans = i;
for (int j = 1; j <= num[i]; ++j)
{
scanf("%d", &x);
s[i].push_back(x);
}
}
for (int i = 1; i < n; ++i)
{
scanf("%d%d", &x, &y);
e[++tot] = edge{y, head[x]}, head[x] = tot;
e[++tot] = edge{x, head[y]}, head[y] = tot;
}
if (num[ans])
cout << num[ans] << '\n';
else
cout << 1 << '\n';
dfs(1, 1);
for (int i = 1; i <= m; ++i)
if(color[i])
printf("%d ", color[i]);
else
printf("1 ");
}
int main()
{
AC();
return 0;
}