注意到每种冰激凌在树上的结点是连通的。如果整棵树是一条链的话,那么问题就转化成了经典的一维区间染色问题。
所以我们要考虑如何把链上的情况推广到树上的情况。
一开始我想把欧拉序转换成区间来做,然而发现并不可行,因为一颗连续的子树可能会形成许多个连续的区间,需要另辟蹊径。
任选一个结点作为树根,可以发现,从根节点到任意叶子结点所形成的链都可以转化成一维的区间染色问题,且各个分支互不影响,所以从根节点dfs一遍,每次贪心选择没有出现过的最小颜色就行了。
注意一个坑点:有的冰激凌可能不会在树上出现,需要单独染色。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=3e5+10,inf=0x3f3f3f3f; 5 int hd[N],ne,n,m,c[N],lg[N],col[N]; 6 vector<int> vec[N]; 7 struct E {int v,nxt;} e[N<<1]; 8 void link(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;} 9 int lb(int x) {return x&-x;} 10 void add(int u,int x) {for(; u<=m; u+=lb(u))c[u]+=x;} 11 int getmi() {int u=0; for(int i=1<<lg[m]; i; i>>=1)if(u+i<=m&&c[u+i]==i)u+=i; return u+1;} 12 void dfs(int u,int f) { 13 for(int x:vec[u])if(col[x])add(col[x],1); 14 for(int x:vec[u])if(!col[x])col[x]=getmi(),add(col[x],1); 15 for(int x:vec[u])add(col[x],-1); 16 for(int i=hd[u]; ~i; i=e[i].nxt) { 17 int v=e[i].v; 18 if(v==f)continue; 19 dfs(v,u); 20 } 21 } 22 int main() { 23 lg[0]=-1; 24 for(int i=1; i<N; ++i)lg[i]=lg[i>>1]+1; 25 memset(hd,-1,sizeof hd),ne=0; 26 scanf("%d%d",&n,&m); 27 for(int i=1; i<=n; ++i) { 28 int k; 29 scanf("%d",&k); 30 while(k--) { 31 int x; 32 scanf("%d",&x); 33 vec[i].push_back(x); 34 } 35 } 36 for(int i=1; i<n; ++i) { 37 int u,v; 38 scanf("%d%d",&u,&v); 39 link(u,v); 40 link(v,u); 41 } 42 dfs(1,0); 43 for(int i=1; i<=m; ++i)if(!col[i])col[i]=1; 44 int ans=0; 45 for(int i=1; i<=m; ++i)ans=max(ans,col[i]); 46 printf("%d ",ans); 47 for(int i=1; i<=m; ++i)printf("%d%c",col[i]," "[i==m]); 48 return 0; 49 }