zoukankan      html  css  js  c++  java
  • Codeforces 1286B. Numbers on Tree题解

    题目链接:Codeforces 1286B. Numbers on Tree
    题目大意:博主太懒了不想打了,大家去洛谷上看吧
    题解:首先,我们考虑无解的情况,很明显,当有一个点的(c_i)大于等于这个点的字数大小时,肯定无解。否则,当每个节点的值不同时,一定有解,证明显然。
    题目中的限制是每个数在(1) ~ (10^9)之间,但是没有意义,可以离散化变成(1,2,cdots,n)的(虽然说我直接构造1n了~)。注意到这一题的权值与答案没有关系,我们所需的仅仅只是在每个权值不同时它们之间的大小关系
    很明显,叶子节点之间的大小关系时不影响答案的,那么我们可以给叶子节点之间的大小随便选。那么对于每一个非叶节点来说,在它的子树之间的大小全部被处理出来时,它只需要在将子树中的两个点拿出来,将自己的权值放在它们之间就可以了。
    如果一开始就给节点赋值,最后很有可能会导致某个节点插在了两个相邻的权值之间,会出现浮点数,这很讨厌,所以,我们一开始不给节点赋值,只考虑维护权值之间的大小关系。
    思考一种能够维护数列大小关系的数据结构,支持动态插入一个元素,要求插入时间复杂度不超过(O(n)),查找时间复杂度不超过(O(n))
    这样的数据结构一个都想不出来就退役吧~~,我们有三个候选数据结构,分别是:

    1. 数组,单次插入(O(n)),单次查找(O(logn))
    2. 链表,单次插入(O(1)),单次查找(O(n))
    3. 平衡树,单次插入(O(logn)),单次查找(O(logn))

    我选用的是第二种,因为我觉得在动态插入时,链表更加直观。
    所以到这里,思路就很清晰了,先将所有叶子节点用链表链接起来,然后考虑它们的父亲插在哪儿,再往上推一层,……,这样下去,就可以得到所有节点之间的大小关系,然后,遍历一遍链表,按大小关系赋值即可,时间复杂度(O(n^2))

    下面是代码:

    #include <cstdio>
    const int Maxn=2000;
    int sz[Maxn+5];
    int fa[Maxn+5];
    int head[Maxn+5],arrive[Maxn+5],e_nxt[Maxn+5],tot;
    void add_edge(int from,int to){
    	arrive[++tot]=to;
    	e_nxt[tot]=head[from];
    	head[from]=tot;
    }
    int n,Root;
    int c[Maxn+5];
    int nxt[Maxn+5],pre[Maxn+5];
    int lea[Maxn+5],lea_len;
    int a[Maxn+5];
    int dfn[Maxn+5],out[Maxn+5],dfn_tot;
    void init_dfs(int u){
    	sz[u]=1;
    	for(int i=head[u];i;i=e_nxt[i]){
    		int v=arrive[i];
    		init_dfs(v);
    		sz[u]+=sz[v];
    	}
    	if(sz[u]==1){
    		lea[++lea_len]=u;
    	}
    }
    void dfs(int u){
    	dfn[u]=++dfn_tot;
    	for(int i=head[u];i;i=e_nxt[i]){
    		int v=arrive[i];
    		dfs(v);
    	}
    	out[u]=dfn_tot;
    	if(sz[u]==1){
    		return;
    	}
    	int pos=0;
    	int num=0;
    	while(num<c[u]||(num==c[u]&&c[u]!=sz[u]-1)){
    		pos=nxt[pos];
    		if(dfn[u]<=dfn[pos]&&out[u]>=dfn[pos]){
    			num++;
    		}
    	}
    	if(c[u]==sz[u]-1){
    		pos=nxt[pos];
    	}
    	int las=pre[pos];
    	nxt[u]=pos;
    	pre[u]=las;
    	nxt[las]=u;
    	pre[pos]=u;
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%d%d",&fa[i],&c[i]);
    		if(fa[i]==0){
    			Root=i;
    		}
    		add_edge(fa[i],i);
    	}
    	init_dfs(Root);
    	lea[lea_len+1]=n+1;
    	for(int i=1;i<=lea_len;i++){
    		pre[lea[i]]=lea[i-1];
    		nxt[lea[i]]=lea[i+1];
    	}
    	nxt[0]=lea[1];
    	pre[n+1]=lea[lea_len];
    	for(int i=1;i<=n;i++){
    		if(c[i]>=sz[i]){
    			puts("NO");
    			return 0;
    		}
    	}
    	dfs(Root);
    	int id_tot=0;
    	int pos=nxt[0];
    	while(pos!=n+1){
    		a[pos]=++id_tot;
    		pos=nxt[pos];
    	}
    	puts("YES");
    	for(int i=1;i<=n;i++){
    		printf("%d ",a[i]);
    	}
    	puts("");
    	return 0;
    }
    
  • 相关阅读:
    7.1类模板
    异质链表
    8.1多态性
    8.2虚函数
    error: C2664: “zajiao::zajiao(const zajiao &)”: 无法将参数 1 从“const char [12]”转换为“char *”
    #include <QPushButton>
    6.3多重继承
    华为集群后killsql命令和查看mr占用的磁盘空间
    linux的逻辑运算符
    test命令
  • 原文地址:https://www.cnblogs.com/withhope/p/12285151.html
Copyright © 2011-2022 走看看