• 【AT4114】[ARC095D] Permutation Tree(简单题)


    点此看题面

    • 对于一个长度为(n)的排列(p)中的每一个(i),找到最大的(j)满足(p_j<p_i),在(i,j)间连边,由此得到出一棵树。
    • 给定一棵(n)个点的树,要求构造一个字典序最小的排列,使得生成的树与给定树同构,或判无解。
    • (nle10^5)

    生成树的本质

    考虑它的生成方式,我们按(p_i)从小到大插入序列。

    那么每次最大的小于当前(p_i)(p_j),就是序列中的最后一个数。

    而当前数可以插入到序列中的任意位置,也就是说既可以放在最后面成为新的最后一个数,也可以放在前面。

    总结一下,抛开这个所谓的排列,就相当于每次我们有两个选择:依旧与原本的点连边,或是与上一个点连边。

    那么这样建出来的一棵树,就是一条链,每个点上挂了若干个叶节点。

    最优的构造方案

    首先如果给定树不能表示成这种形式,即非叶节点并不在一条链上,显然无解。

    否则,一条链无非两种方向,我们大可分别求出两种方案下的答案,取个较优解即可。这样一来我们就能确定出根节点,那么父子关系也就确立了。

    考虑一个非叶节点,它必然需要在它的祖先及祖先的其与子节点都填完之后才能填。

    它的子节点们权值必须要全比它大,且除了非叶节点儿子要填在它之后外,所有的叶节点儿子都必须填在它之前(否则后续点就会向在它之后的儿子连边)。

    因此假设当前点能取的权值为(nw),有(s_x)个叶节点儿子,我们的填法必然是先填上(nw+1sim nw+s_x)(叶节点儿子们),再填上(nw)(当前点)。

    最后还要注意一下,把链首非叶节点和链尾非叶节点的一个叶节点儿子也算到链上(也就是把它们当成非叶节点来对待),肯定能得到更优的答案。

    代码:(O(n))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    using namespace std;
    int n,d[N+5],ee,lnk[N+5];struct edge {int to,nxt;}e[N<<1];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
    	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
    	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    	Tp I void write(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc(' ');}
    }using namespace FastIO;
    int c,s[N+5];I void dfs(CI x,CI lst=0)//遍历一遍,依次记录每个非叶节点的叶节点儿子数
    {
    	s[++c]=d[x]-2;for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&d[e[i].to]^1&&(dfs(e[i].to,x),0);
    }
    int ans1[N+5],ans2[N+5];I void Get(int* ans)//求出序列
    {
    	for(RI i=0,j=1,k;i<=c+1;++i) {for(k=1;k<=s[i];++k) ans[j+k-1]=j+k;ans[j+s[i]]=j,j+=s[i]+1;}//先填叶节点儿子,再填当前点
    }
    I void Cmp(int* ans1,int* ans2)//两种方案比较,取字典序较小的
    {
    	for(RI i=1;i<=n;++i) if(ans1[i]^ans2[i]) {if(ans1[i]<ans2[i]) return;break;}//最早的不同位,若ans1更小直接return
    	for(RI i=1;i<=n;++i) ans1[i]=ans2[i];//ans2更小,令ans1=ans2
    }
    int main()
    {
    	RI i,j,x,y;for(read(n),i=1;i^n;++i) read(x,y),add(x,y),add(y,x),++d[x],++d[y];
    	if(n==2) return puts("1 2"),0;//特判n=2
    	RI rt,t;for(i=1;i<=n;++i) if(d[i]^1)
    	{
    		for(t=0,j=lnk[i];j;j=e[j].nxt) d[e[j].to]^1&&++t;if(t>2) return puts("-1"),0;//如果非叶节点不是链,无解
    		if(t<=1) rt=i;//以度数=1的点为根(之所以写≤1是因为菊花图可能只有一个非叶节点)
    	}
    	dfs(rt),Get(ans1),reverse(s,s+c+2),Get(ans2);//先dfs一遍求出链上每个点的叶节点儿子数,然后按两种方向分别求解
    	for(Cmp(ans1,ans2),i=1;i<=n;++i) write(ans1[i]);return clear(),0;//输出字典序较小的答案
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    基准测试工具 pgbench 之一初步测试
    postgresql 9.1 kernel.shm 设置不当导致 FATAL: could not create shared memory segment
    centos 6.8 + postgresql 9.6 + make
    plpgsql 执行块
    postgresql 10.1 分区表之 list 分区
    debian 各个版本的代号
    debian 8.2 bond 双网卡
    centos 6.8 + postgresql 9.6 + adminpack
    centos 6.8 + postgresql 9.6 + pldebugger/pldbgapi
    postgresql 10.1 参数的初步了解 Preset Options
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/AT4114.html
Copyright © 2020-2023  润新知