题意:
给定n个点的树,要求给每个节点一个’A’-'Z’的字符
使得树满足:
对于任意两点a和b,如果点a和点b上面的字符一样,
那么a到b的路径上必须存在一个点c,
点c需要满足字符大于点a和b上的字符.
(本题中’A’最大,'Z’最小)
输出方案,如果无解输出impossible
数据范围:n<=1e5
解法:
显然最多只有一个点标记'Z',因为没有比'Z'更大的字符了
如果存在两个'Z',没办法在他们中间放更大的
考虑在哪个位置放'Z'最优,
如果我们在点x位置放置了'Z',那么对于点x的所有子树,都不能放'Z',
问题变成用'A'到'Y'处理点x的每棵子树,这是一个子问题.
为了有解,子问题的规模不宜太大,容易想到树的重心,
用树的重心拆分树,最大子树的规模最小,
如果不用重心拆的话,层数过多,容易无解.
因此每次找树的重心,拆分整棵树,然后对每棵子树递归求重心即可,
递归找重心是经典的点分治问题.
每次按照重心拆分,子树规模<=n/2,因此递归总层数为log(n),
第一层用'Z',第二层用'Y',那么超过26层无解,
因为log(1e5)=11<26,所以一定有解
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=1e5+5;
int head[maxm],nt[maxm<<1],to[maxm<<1],cnt;
int sz[maxm],son[maxm];
int mark[maxm];
char ans[maxm];
int size;
int root;
int n;
void add(int x,int y){
cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;
}
void getroot(int x,int fa){
sz[x]=1;
son[x]=0;
for(int i=head[x];i;i=nt[i]){
int v=to[i];
if(v==fa||mark[v])continue;
getroot(v,x);
sz[x]+=sz[v];
son[x]=max(son[x],sz[v]);
}
son[x]=max(son[x],size-sz[x]);
if(son[root]>son[x]){
root=x;
}
}
void divide(int x,char p){
ans[x]=p;
mark[x]=1;
for(int i=head[x];i;i=nt[i]){
int v=to[i];
if(mark[v])continue;
son[root=0]=size=sz[v];
getroot(v,-1);
divide(root,p+1);
}
}
signed main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
int a,b;scanf("%d%d",&a,&b);
add(a,b);add(b,a);
}
son[root=0]=size=n;
getroot(1,-1);
divide(root,'A');
for(int i=1;i<=n;i++){
printf("%c ",ans[i]);
}
return 0;
}