1 条题解

  • 0
    @ 2025-8-24 21:55:28

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar zzxLLL
    Retired

    搬运于2025-08-24 21:55:28,当前版本为作者最后更新于2023-08-24 08:49:35,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    挺有意思一道题。提供一种更方便理解(?)的做法。


    要求字典序最小,我们可以贪心地确定第一个数,即满足度数 2\leq 2 的编号最小的节点。

    设这个节点为 uu,将 uu 钦定为最左边的节点,先讨论 uu 度数为 22 的情况,那么与他相连的两个节点,一个是右儿子 vv,一个是父亲 ff

    先不考虑字典序最小。

    此时我建出一颗新的树,uu 为根节点,让 vvuu 的左儿子,ffuu 的右儿子,其余的关系不变。若将在 uu 处的遍历顺序设置为前序遍历,那么在 uu 处就会按照 uvwu - v - w 的顺序遍历。那么在 uu 处这其实和原树的遍历顺序是相同的。

    回到原树,vvuu 的儿子,那么在 vv 的子树中应该是中序遍历,这点在新树上也一样,故新树上 vv 及其子树都采取中序遍历。

    ffuu 的父亲,当原树遍历完 uuvv 的子树之后,它们就不影响后面的中序遍历的,可以删去。删去之后,ff 的情况就和 uu 相同了,即都被钦定为最左边的节点。这是一个子问题,让 ff 成为新的 uu,递归解决。

    再是度数为 11 的情况,与 uu 相连的节点为 ww,那么 ww 成为新树上 uu 的左儿子,在原树上 ww 就是 uu 的右儿子;ww 成为新树上 uu 的右儿子,在原树上 ww 就是 uu 的父亲。

    所以总的策略就是:先将 uu 提为根节点;然后新树按照如下规则 dfs:

    • 若当前节点采用前序遍历,那么其左儿子采用中序遍历,右儿子采用前序遍历

    • 若当前节点采用中序遍历,那么其左右儿子都采用中序遍历。

    可以证明这样形成的遍历序列就是原树的中序遍历序列。

    下面考虑怎么使字典序更小。即如何安排新树上的左右儿子。

    先以 uu 为根,求出 dpxdp_xxx 的子树内度数 2\leq 2 的编号最小的节点编号。

    然后 dfs 过程中分类讨论,当前节点为 xx

    1. xx 处为前序遍历

    若新树上 xx11 个儿子 ss,那么如果 dps<sdp_s < s 就将 ss 作为新树上 xx 的左儿子,否则作为 xx 的右儿子。

    若新树上 xx22 个儿子 s1,s2s_1, s_2,那么如果 dps1<dps2dp_{s_1} < dp_{s_2} 那么将 s1s_1 作为新树上 xx 的左儿子,s2s_2 作为 xx 的右儿子。否则 s2s_2 作为左儿子,s1s_1 作为右儿子。

    1. xx 处为中序遍历

    若新树上 xx11 个儿子 ss,那么如果 dps<xdp_s < x 就将 ss 作为 xx 的左儿子,否则作为 xx 的右儿子。

    若新树上 xx22 个儿子 s1,s2s_1, s_2,那么如果 dps1<dps2dp_{s_1} < dp_{s_2} 则将 s1s_1 作为新树上 xx 的左儿子,s2s_2 为右儿子。否则 s2s_2 为左儿子,s1s_1 为右儿子。

    容易证明这样贪心是正确的。按照顺序输出即可。

    #include<cstdio>
    #include<vector>
    #include<algorithm>
    const int M=1e6+10;
    const int inf=1e9+7;
    
    int n,rt,deg[M],f[M];
    std::vector<int>g[M];
    
    void dfs(int u,int fa,bool type){
    	// type = 0 : 前序遍历
    	// type = 1 : 中序遍历
    	std::vector<int>nxt;
    	for(int v:g[u])
    		if(v!=fa) nxt.push_back(v);
    	if(!type){
    		// 根 - 左子树 - 右子树
    		printf("%d ",u);
    		if(nxt.size()==1){
    			if(f[nxt[0]]<nxt[0]){
    				dfs(nxt[0],u,1);
    			}else{
    				dfs(nxt[0],u,0);
    			}
    		}else if(nxt.size()==2){
    			if(f[nxt[0]]<f[nxt[1]]){
    				dfs(nxt[0],u,1);
    				dfs(nxt[1],u,0);
    			}else{
    				dfs(nxt[1],u,1);
    				dfs(nxt[0],u,0);
    			}
    		}
    	}else{
    		// 左子树 - 根 - 右子树
    		if(nxt.size()==0){
    			printf("%d ",u);
    		}else if(nxt.size()==1){
    			if(f[nxt[0]]<u){
    				dfs(nxt[0],u,1);
    				printf("%d ",u);
    			}else{
    				printf("%d ",u);
    				dfs(nxt[0],u,1);
    			}
    		}else if(nxt.size()==2){
    			if(f[nxt[0]]<f[nxt[1]]){
    				dfs(nxt[0],u,1);
    				printf("%d ",u);
    				dfs(nxt[1],u,1);
    			}else{
    				dfs(nxt[1],u,1);
    				printf("%d ",u);
    				dfs(nxt[0],u,1);
    			}
    		}
    	}
    }
    
    void dp(int u,int fa){
    	f[u]=(deg[u]<=2?u:inf);
    	for(int v:g[u]){
    		if(v==fa) continue;
    		dp(v,u);
    		f[u]=std::min(f[u],f[v]);
    	}
    }
    
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%d",&deg[i]);
    		for(int j=1,x;j<=deg[i];j++){
    			scanf("%d",&x);
    			g[i].push_back(x);
    		}
    		if(deg[i]<=2&&!rt) rt=i;
    	}
    	dp(rt,0);
    	dfs(rt,0,0);
    	return 0;
    }
    
    • 1

    信息

    ID
    2958
    时间
    1000ms
    内存
    500MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者