1 条题解

  • 0
    @ 2025-8-24 22:49:33

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar do_while_true

    搬运于2025-08-24 22:49:33,当前版本为作者最后更新于2023-09-10 08:41:39,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    orz negiizhao

    自底向上确定每个点的所有出边上挂的字符,那么问题就是比较 x,yx,y 两个子树的字典序大小。直接一起往下 dfs,先找到标记点的子树更小,如果 dfs 过程中一棵树找完了而另一棵树没找完并且还没确定大小,这时还没找完的那棵树应当排到前面。在递归的最浅层也就是比较 x,yx,y 时最后特判下标号大小即可。

    这里排序使用归并排序 stable_sort,现在来分析一下复杂度:

    单独考虑每一层:在归并两个队头分别为 u,vu,v 的子树序列 p,qp,q 时,花费 min(sizeu,sizev)\min(size_u,size_v) 的时间代价,将其中之一 pop 掉。先把代价放缩成 pop 掉的那个子树的 sizesize,但如果 pop 掉的是重儿子是把代价记成没有被 pop 掉的那个轻儿子上。这样每个轻儿子计算代价时最多被计算两次,所以同一层所花费的是轻儿子子树和级别的。再算上归并排序一共有 log\log 层,那么时间复杂度就是 O(nlog2n)\mathcal{O}(n\log^2 n)

    
    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
    上传者