zoukankan      html  css  js  c++  java
  • [ARC095 F]Permutation Tree

    最近刷AtCoder,觉得刷得挺爽的,每次的F题都有一定难度,但是却还是挺可做的>_<反正AtCoder对于我这种英文不好的选手来说非常友善,看一眼就读完题面了

    题意

    Takahashi有一种能力以以下步骤用一个排列((p_1,p_2,cdots,p_n))来生成一棵树:

    首先,按照以下操作准备顶点(1cdots N)。对于顶点(i)

    • 如果(p_i=1),什么都不做
    • 如果(p_i eq 1),让(j')表示最大的满足(p_j<p_i)(j)。在(j')(i)之间连一条边

    给出按照这个过程建立的一棵树,判定是否能够用另外一个排列做出一棵与此同构的树,问这个要求字典序最小的排列应该是什么,无解输出(-1)
    (1leq nleq10^5)

    分析

    考虑我们将这个操作的过程改写成:一开始全是黑点,然后我们将(p_i)按照升序排列,考虑为原来的(i)。然后我们令一个(max=-1),考虑从左到右扫一遍,如果发现(i>max),那么将(i)染红,并将(max)替换成(i);如果当前的(p_i eq 1),那么连边(注意一下,原来的过程那里,(j)并不一定小于(i)……这里是对于全局的……)。
    这个过程符合原来的性质,因为每次和某个点连接的红点,肯定是比(p_i)小,而且最接近。这时候我们会发现,我们得到的这个红点之间的链,相当于一条“直径”的东西,然后剩下的黑点都向着这个直径上连边,而且显然黑点之间没有边。
    实际上按照原来的过程也可以感受出来,某一个(p_i)很小但是(i)很大的点,可能会能够“占领”一大堆点,直到某一个(i)更大的点出现。
    这样的图是一个“毛虫图”。如果给定的图不是一个“毛虫图”,那肯定不对,这和我们刚刚证明的不一样。怎么验证呢?我们看这条链,很容易想到就是找直径,然后我们检测一下就好了,对于直径上的点每个都搜上一层,看看它是不是只会连到直径这一个点上。
    那么怎么构造字典序最小的答案?事实上这也很简单。考虑设直径上的点伸出来的边依次为(k_i)条,不难看出此时我们只要将排列({1,2,3cdots l})做以下操作:每次对于一个(i),将(sumlimits_{j=0}^{i+1}(k_i+1))位置的数右移(k_i)个的位置,就会得到字典序最小的答案。
    不过注意我们直径上面点的顺序可能有两种,都需要试一下。

    # include<bits/stdc++.h>
    # define rep(i,l,r) for(int i=l;i<=r;++i)
    using namespace std;
    const int N=1e5+5;
    vector<int>lk[N];
    int fa[N],deep[N];
    int s[N];
    int np[N],p1[N],p2[N];
     
    void dfs(int x,int fr){
    	fa[x]=fr;deep[x]=deep[fr]+1;
    	for(auto y:lk[x])
    	if(y!=fr)dfs(y,x);
    }
     
    int main(){
    	int n;
    	cin>>n;
    	rep(i,1,n){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		lk[x].push_back(y);lk[y].push_back(x);
    	}
    	dfs(1,0);
    	int x=1;
    	rep(i,2,n)
    	if(deep[i]>deep[x])x=i;
    	dfs(x,0);
    	int y=1;
    	rep(i,2,n)
    	if(deep[i]>deep[y])y=i;
    	for(int i=y;i;i=fa[i])s[i]=1;
    	rep(i,1,n)
    	if(!s[i]&&++s[fa[i]]==1){
    		puts("-1");
    		exit(0);
    	}
    	vector<int>a;
    	for(int i=y;i;i=fa[i])a.push_back(s[i]);
    	int top=0;
    	for(auto i:a){
    		np[top+1]=top+i;
    		rep(j,1,i-1)np[top+1+j]=top+j;
    		top+=i;
    	}
    	rep(i,1,n)p1[np[i]]=i;
    	top=0;
    	for(auto it=a.end();--it>=a.begin();){
    		int i=*it;
    		np[top+1]=top+i;
    		rep(j,1,i-1)np[top+1+j]=top+j;
    		top+=i;
    	}
    	rep(i,1,n)p2[np[i]]=i;
    	int i=1;
    	while(p1[i]==p2[i]&&i<=n)++i;
    	if(p1[i]<p2[i])
    		rep(i,1,n)printf("%d ",p1[i]);
    	else
    		rep(i,1,n)printf("%d ",p2[i]);
    }
    
  • 相关阅读:
    抓住六个点,谈唯品会的峰值系统应对实践
    从服务端架构设计角度,深入理解大型APP架构升级
    腾讯微信技术总监周颢:一亿用户增长背后的架构秘密
    App服务端架构变迁
    微服务、SOA 和 API对比与分析
    QPS从0到4000请求每秒,谈达达后台架构演化之路
    重构心法——打造高质量代码
    [转]使用 C++11 编写 Linux 多线程程序
    [转]编译防火墙——C++的Pimpl惯用法解析
    [转]C++ 取代switch的三种方法
  • 原文地址:https://www.cnblogs.com/wendavid/p/8871654.html
Copyright © 2011-2022 走看看