loj#2324. 「清华集训 2017」小 Y 和二叉树(贪心+分类讨论)

15 篇文章 0 订阅
9 篇文章 0 订阅

题面在这里

题意:

给出一棵每个点度数不超过3的无根树,节点编号为 1 1 ~n
你需要确定根和每个节点的左右儿子,使其成为一棵二叉树。
问所有可得到的二叉树中中序遍历字典序最小的树的中序遍历。
n1,000,000 n ≤ 1 , 000 , 000

做法:

这题似乎当时集训队全场ac
首先找出一个最小的度 2 ≤ 2 的点作为最左边的拐角点,也就是中序遍历的第一个数。
然后把这个点当做根, f[u] f [ u ] 表示以 u u 为根的子树中中序遍历开头的最小值。
每次确定一下这个点u连向的点当做 u u 的父亲还是儿子,根据f[u]来分类讨论。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
using namespace std;
typedef long long ll;
inline ll read() {
    char ch = getchar(); ll x = 0; int op = 1;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') op = -1;
    for(; isdigit(ch); ch = getchar()) x = (x<<1)+(x<<3)+ch-'0';
    return x*op;
}
const int N = 2000010;
int n, cnt, flag, chk, tot, rt;
int head[N], d[N], ans[N], f[N];
struct edge {
    int to, nxt;
    edge() {}
    edge(int x, int y) { to = x; nxt = y; }
} e[N];
inline void addedge(int x, int y) { e[++ cnt] = edge(y, head[x]); head[x] = cnt; }
inline void dfs(int u, int lst) {
    for(int i = head[u], v; i; i = e[i].nxt) if((v = e[i].to) != lst) {
        dfs(v, u); f[u] = min(f[u], f[v]);//f[u]表示以u为根的子树中中序遍历开头的最小值
    }
}
inline void get(int u, int lst) {
    for(int i = head[u], v; i; i = e[i].nxt) if((v = e[i].to) != lst && f[v] == f[u]) get(v, u);
    ans[++ tot] = u;
    for(int i = head[u], v; i; i = e[i].nxt) if((v = e[i].to) != lst && f[v] != f[u]) get(v, u);
}
int main() {
    /*freopen("binary.in", "r", stdin);
    freopen("binary.out", "w", stdout);*/
    n = read();
    for(int i = 1; i <= n; i ++) {
        d[i] = read();
        for(int j = 1; j <= d[i]; j ++) {
            int x = read(); addedge(x, i);
        }
    }
    for(int i = 1; i <= n; i ++)
        if(d[i] <= 2) { rt = i; break; }
    for(int i = 1; i <= n; i ++) if(d[i] <= 2) f[i] = i; else f[i] = n+1;
    dfs(rt, 0);// for(int i = 1; i <= n; i ++) printf("%d ", f[i]); puts("");
    for(int u = rt, lst = 0; ; ) {
        ans[++ tot] = u; int c[5], ch = 0;
        for(int i = head[u], v; i; i = e[i].nxt) if((v = e[i].to) != lst) c[++ ch] = v;
        if(!ch) break;
        if(ch == 1) {
            if(f[c[1]] < c[1]) { get(c[1], u); break; }
            else { lst = u; u = c[1]; }
        }
        if(ch == 2) {
            if(f[c[1]] > f[c[2]]) swap(c[1], c[2]);
            get(c[1], u); lst = u; u = c[2];
        }
    }
    for(int i = 1; i <= n; i ++) printf("%d ", ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值