zoukankan      html  css  js  c++  java
  • 「清华集训 2017」小 Y 和二叉树

    「清华集训 2017」小 Y 和二叉树

    原题数据好像没有卡这个情况

    5
    1 2
    2 1 3
    3 2 4 5
    1 3
    1 3
    

    输出是

    1 2 3 4 5
    

    首先考虑一个(O(n^2))的暴力:

    枚举一个点为根,向下展开树,此时只需要决策左儿子和右儿子的顺序

    当两个子树都存在时,由于两个子树包含的元素不同,所以可以直接把 两个子树序列首较小 (显然不会出现相同的情况) 的一个放在前面即可

    实际上我们可以发现,这样得到的序列第一个元素必然是 编号最小的不同时包含左右儿子 的结点

    不妨称固定根之后,这样的结点为叶子

    [ ]

    显然的性质:任何一个度数(leq 2)的点可以作为答案序列的第一个点

    设原树上最小的(leq 2)的点为(root),接下来对于(root)的不同情况讨论,要在强制(root)为序列首的情况下,求得最小的序列

    不妨先预处理出结点(u)子树里最小的叶子(mi_u)

    1.没有相邻结点,结束

    2.有两个相邻结点,此时要使自己为序列首,必然有一个结点是自己的父亲,有一个结点是自己的右儿子

    右儿子会被先遍历到,所以可以直接考虑比较两个相邻结点 作为 右儿子时谁的序列首 较小

    即比较两个子树中最小的叶子即可

    3.只有一个相邻结点,设其为(v)

    此时要使得自己为序列第一个,只有两种可能,此时同样可以考虑比较序列首元素

    3-1.让相邻结点作为自己的父亲,此时下一个元素一定是(v)

    3-2.让相邻结点作为自己的右儿子,此时下一个元素一定是(mi_v)

    如果(v e mi_v),显然好决策

    (v=mi_v)时,必然满足(v)是一个叶子,此时如果将(v)放在父亲上,(v)的另一个相邻结点(如果存在)

    可以放在(v)的父亲或者是(v)的右子树,如果放在(v)的右子树,那么这种情况与(v)被放在(u)的子树等价

    也就是说,把(v)放在父亲可以决策出的序列情况,包含了把(v)放在右儿子的情况

    所以这种情况也应当把(v)放在父亲上

    实现上,不妨用两个(dfs)处理最后的决策,一个强制当前结点(u)为序列首,一个求出(u)子树的最优方案

    在代码里就是(Solve,dfs\_get)

    #include<bits/stdc++.h>
    using namespace std;
    
    #define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
    #define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
    template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
    
    char IO;
    int rd(){
    	int s=0,f=0;
    	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    	do s=(s<<1)+(s<<3)+(IO^'0');
    	while(isdigit(IO=getchar()));
    	return f?-s:s;
    }
    
    const int N=1e6+10;
    
    int n;
    int c[N],s[N][3];
    int mi[N];
    
    int rt=1e9;
    void dfs(int u,int f) {
    	mi[u]=1e9;
    	int cnt=0;
    	rep(i,0,c[u]-1) if(s[u][i]!=f) {
    		int v=s[u][i]; cnt++;
    		dfs(v,u),cmin(mi[u],mi[v]);
    	}
    	if(cnt<=1) cmin(mi[u],u);
    }
    
    int vis[N];
    void dfs_get(int u) {
    	vis[u]=1;
    	int a=-1,b=-1;
    	rep(i,0,c[u]-1) if(!vis[s[u][i]]) {
    		int v=s[u][i];
    		if(~a) b=v;
    		else a=v;
    	}
    	if(a==-1) printf("%d ",u);
    	else if(b==-1) {
    		if(mi[a]<u) dfs_get(a),printf("%d ",u);
    		else printf("%d ",u),dfs_get(a);
    	} else {
    		if(mi[a]>mi[b]) swap(a,b);
    		dfs_get(a),printf("%d ",u),dfs_get(b);
    	}
    }
    
    void Solve(int u) {
    	int cnt=0;
    	rep(i,0,c[u]-1) if(!vis[s[u][i]]) cnt++;
    	vis[u]=1,printf("%d ",u);
    	if(cnt==1) {
    		rep(i,0,c[u]-1) if(!vis[s[u][i]]) {
    			int v=s[u][i];
    			if(v>mi[v]) dfs_get(s[u][i]);
    			else Solve(v);
    		}
    	} else if(cnt==2) {
    		int a=-1,b=-1;
    		rep(i,0,c[u]-1) if(!vis[s[u][i]]) {
    			int v=s[u][i];
    			if(~a) b=v;
    			else a=v;
    		}
    		if(mi[a]>mi[b]) swap(a,b);
    		dfs_get(a),Solve(b);
    	}
    }
    
    int main(){
    	//freopen("binary.in","r",stdin),freopen("binary.out","w",stdout);
    	n=rd();
    	if(n==1) return puts("1"),0;
    	rep(i,1,n) {
    		c[i]=rd();
    		if(c[i]<=2) cmin(rt,i);
    		rep(j,0,c[i]-1) s[i][j]=rd();
    	}
    	dfs(rt,0);
    	Solve(rt);
    }
    
  • 相关阅读:
    关于IQKeyboardManager的使用
    iOS 关于退出键盘两种方法和避免遮挡
    iOS获取各种数据方法整理以及IDFA与IDFV使用环境
    npm安装模块 -g和--save和--save-dev的区别
    最详细的原生js实现ajax的封装
    js中Math对象常用的属性和方法
    js中的兼容问题汇总
    js中数组方法及分类
    浅析js中的this
    js中的兼容
  • 原文地址:https://www.cnblogs.com/chasedeath/p/13949319.html
Copyright © 2011-2022 走看看