题解 CF1214H 【Tiles Placement】
CF1214H Tiles Placement解题报告:
更好的阅读体验
题意
- 给定一棵
n 个点的树,可以用k 种颜色染色; - 你需要判断是否存在一种染色方案满足任意长度为
k 的链必须包含k 种颜色; - 若有方案,则输出"Yes",并任意输出一种方案;否则输出"No"。
-
分析
最近听了一个神仙讲了这道题,就来做一做。
一道找规律神题,现场推出了和正解等价的规律,但是还是不会。
先给出结论:
#include<stdio.h>
#define inf 1000000000
const int maxn=200005,maxm=maxn<<1;
int n,m,e,s1,s2,s,maxx,col,flg,mid;
int start[maxn],to[maxm],then[maxm];//链式前向星
int dep[maxn],f[maxn],ans[maxn],flen[maxn],slen[maxn];//深度,递归时的父亲,染的颜色,最长链,次长链
inline void add(int x,int y){//加边
then[++e]=start[x],start[x]=e,to[e]=y;
}
void dfs(int x,int last){
f[x]=last,dep[x]=dep[last]+1;
flen[x]=slen[x]=0;
if(dep[x]>maxx)
maxx=dep[x],s=x;
for(int i=start[x];i;i=then[i]){
int y=to[i];
if(y==last)
continue;
dfs(y,x);
}
}
void getlen(int x,int last){
for(int i=start[x];i;i=then[i]){
int y=to[i];
if(y==last||ans[y])
continue;
getlen(y,x);
if(flen[y]+1>flen[x])
slen[x]=flen[x],flen[x]=flen[y]+1;
else if(flen[y]+1>slen[x])
slen[x]=flen[y]+1;
if(slen[y]+1>slen[x])
slen[x]=slen[y]+1;
}
}
void solve(int x,int c,int t){//染色,其中t=1时颜色为父亲的颜色+1,t=-1时相反
ans[x]=c;
c=(c+t+m-1)%m+1;
for(int i=start[x];i;i=then[i]){
int y=to[i];
if(ans[y]!=0)
continue;
solve(y,c,t);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
maxx=-inf,dfs(1,0),s1=s;//s1为直径一个端点
maxx=-inf,dfs(s,0),s2=s;//s2为直径另一端点
if(m==2){//特判m=2
puts("Yes");
for(int i=1;i<=n;i++)
printf("%d%c",dep[i]%2+1,i==n? '\n':' ');
return 0;
}
for(int i=s2;i!=0;i=f[i]){//对直径染色
col=col%m+1;
if((dep[s1]+dep[s2])/2==dep[i])//找到中点
mid=i;
ans[i]=col;
}
for(int i=s2;i!=0;i=f[i]){//直径上的点向它不向直径扩展的子树中求最长/次长链
getlen(i,0);
if(ans[i]&&flen[i]&&dep[i]+flen[i]>=m&&(dep[s2]-dep[i]+1)+flen[i]>=m){
puts("No");
return 0;
}
}
for(int i=1;i<=n;i++)
if(flen[i]+slen[i]+1>=m){
puts("No");
return 0;
}
for(int i=s2;i!=mid;i=f[i])//直径前半段染色
solve(i,ans[i],-1);
for(int i=mid;i!=0;i=f[i])//直径后半段染色
solve(i,ans[i],1);
puts("Yes");
for(int i=1;i<=n;i++)
printf("%d%c",ans[i],i==n? '\n':' ');
return 0;
}