1 条题解
-
0
自动搬运
来自洛谷,原作者为

do_while_true
水搬运于
2025-08-24 22:49:33,当前版本为作者最后更新于2023-09-10 08:41:39,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
orz negiizhao
自底向上确定每个点的所有出边上挂的字符,那么问题就是比较 两个子树的字典序大小。直接一起往下 dfs,先找到标记点的子树更小,如果 dfs 过程中一棵树找完了而另一棵树没找完并且还没确定大小,这时还没找完的那棵树应当排到前面。在递归的最浅层也就是比较 时最后特判下标号大小即可。
这里排序使用归并排序
stable_sort,现在来分析一下复杂度:单独考虑每一层:在归并两个队头分别为 的子树序列 时,花费 的时间代价,将其中之一 pop 掉。先把代价放缩成 pop 掉的那个子树的 ,但如果 pop 掉的是重儿子是把代价记成没有被 pop 掉的那个轻儿子上。这样每个轻儿子计算代价时最多被计算两次,所以同一层所花费的是轻儿子子树和级别的。再算上归并排序一共有 层,那么时间复杂度就是 .
const int N=200010; int n,m; int fa[N],vis[N],col[N],siz[N]; vi eg[N]; void dfs1(int x){ siz[x]=1; for(auto v:eg[x])dfs1(v),siz[x]+=siz[v]; stable_sort(eg[x].begin(),eg[x].end(),[&](const int &x,const int &y){ function<int(int,int)>cmp=[&](int u,int v){//u<v:1 v<u:-1 u=v:0 if(vis[u]+vis[v]==1)return vis[u]?1:-1; int len=min(eg[u].size(),eg[v].size()); for(int i=0;i<len;i++){ int t=cmp(eg[u][i],eg[v][i]); if(t)return t; } if(siz[u]==siz[v]){ if(u==x&&v==y)return u<v?1:-1; return 0; } else return siz[u]>siz[v]?1:-1; }; return cmp(x,y)==1?1:0; }); int len=eg[x].size(); for(int i=0;i<len;i++)col[eg[x][i]]=i; } void solve(){ read(n,m); for(int i=0;i<=n;i++)vi().swap(eg[i]),vis[i]=0; for(int i=1;i<=n;i++)read(fa[i]),eg[fa[i]].pb(i); for(int i=1,x;i<=m;i++)vis[read(x)]=1; dfs1(0); for(int i=1;i<=n;i++)putchar('a'+col[i]); puts(""); }
- 1
信息
- ID
- 9094
- 时间
- 1000ms
- 内存
- 1024MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者