Codeforces 1247 F. Tree Factory

@vrqq  October 27, 2019
快乐是短暂的,思考让我放下一切,AC的一瞬间还是很快乐的。

解题报告 https://codeforces.com/contest/1247/problem/F

我觉得这题想得出原理,但是写起来还是挺难的。
题:使用最少的步骤,从 bamboo tree 构造出 target normal tree。

存树
每个Node存next_brother,这个node的parent里面,存一个header。
相当于一个链表,文末代码一看便知。

原理1 目前在点p,那么 p->child1子树 和 p->child2子树 在bamboo tree中一定 是独立的
显而易见,在纸上举几个例子,会发现如果 两个子树分别包含的点 水乳交融的话,那么展开这个bamboo一定会耗费更多的步骤。

原理2 若在bamboo tree中,p->child1子树 在 p->child2子树 的前面,那么无论p->child1子树怎么排列,将p->child2提到目标位置的步骤是不变的
这个很关键,举例就是,现在有一颗树如图
total.png
针对Node1这个子树,假设现在bamboo tree上面,已经把node1这个子树defragment完成了。在bamboo tree中,“Node1子树”后面接“Node2子树”,如图,蓝色的是等待放置的下一个点
wait_to_put.png
transf.png
我们发现:无论Node1子树内部如何排列,把 “Node2子树” levitate to “Node1节点兄弟” 的位置,所需要的步骤都是一样的。
那么我们就已知了对于任意一个节点代表的子树:“把这个子树的点整理完” + “把附在这个segment后面的其他树升上去” 的步数 是相同的。

结论
所以我们就只需要针对所有的子树,来计算“仅仅整理这个子树”和“整理+浮起兄弟子树”的步骤分别是多少(代码中 ans_defrag_only 以及 ans_defrag_and_crawled)
然后我们针对每一个点,都尝试算一下哪个子树放到最后合适。
比如上面那个树,针对Node 0,显然Node 7子树放到bamboo的最后比较合适。

同理,根据上面的原理2,对于任何一个点Node p所代表的那一坨树,在bamboo tree中应该是
{p, [segment of p->child1], [segment of p->child2], ..., [segment of p->childtail]}
无论p->child1 \ p->child2 怎么排列,答案都是一样的。

代码如下,去掉注释打印整棵树:

#include <iostream>
#include <cstring>
using namespace std;

const int MAXN = 100010, MAXANS = 1000100;
struct Tree {
    int parent = -MAXN;
    int chhead = -1, bro = -1;
    int ans_defrag_and_crawled, ans_defrag_only;
    int ans_child_tail = -1;
}tree[MAXN];
void appendChild(int fn, int cn) {
    tree[cn].parent = fn;
    tree[cn].bro = tree[fn].chhead;
    tree[fn].chhead = cn;
}


void dfs(int p) {
    for (int ch = tree[p].chhead; ch != -1; ch = tree[ch].bro)
        dfs(ch);

    //ans defragment and crawled: defragment all of its subtree + send the 'visiter' as its brother.
    tree[p].ans_defrag_and_crawled = 0;
    for (int ch = tree[p].chhead; ch != -1; ch = tree[ch].bro)
        tree[p].ans_defrag_and_crawled += tree[ch].ans_defrag_and_crawled; // let child's head to last_child's tail one by one.
    tree[p].ans_defrag_and_crawled++; //send the 'visiter' from its child as its bro, level up +1.

    //ans defragment only: don't have 'visiter', only tidy up all of its subtree
    //ans_defrag_only = min{ child[i].defrag_only + child[other].defrag_and_crawled };
    int sum = tree[p].ans_defrag_and_crawled - 1;
    tree[p].ans_defrag_only = MAXANS;
    for (int ch = tree[p].chhead; ch != -1; ch = tree[ch].bro)
        if (tree[p].ans_defrag_only > sum - tree[ch].ans_defrag_and_crawled + tree[ch].ans_defrag_only) {
            tree[p].ans_defrag_only = sum - tree[ch].ans_defrag_and_crawled + tree[ch].ans_defrag_only;
            tree[p].ans_child_tail = ch;
        }
    if (tree[p].chhead == -1) //special case for leaf node.
        tree[p].ans_defrag_only = 0;
}


int ans_bamboo[MAXN], ans_bamptr = 0;
int ans_op[MAXANS], ans_opptr = 0;
void write_levitate_op(int p, int now_parent) {
    int target_parent = tree[p].parent;
    // cout<<"LEVITATE "<<p<<" from "<<now_parent<<" to "<<target_parent<<endl;
    while(now_parent != target_parent) {
        ans_op[ans_opptr++] = p;
        now_parent = tree[now_parent].parent;
    }
}
void output(int p) {
    // cout<<"Node "<<p<<": ans_defrag_only="<<tree[p].ans_defrag_only<<" ans_defrag_and_crawled="<<tree[p].ans_defrag_and_crawled<<endl;
    // cout<<"\ttail_chid="<<tree[p].ans_child_tail<<endl;
    ans_bamboo[ans_bamptr++] = p;
    for (int ch = tree[p].chhead; ch != -1; ch = tree[ch].bro)
        if (ch != tree[p].ans_child_tail) {
            write_levitate_op(ch, ans_bamboo[ans_bamptr-1]);
            output(ch);
        }
    if (tree[p].ans_child_tail != -1) {
        write_levitate_op(tree[p].ans_child_tail, ans_bamboo[ans_bamptr-1]);
        output(tree[p].ans_child_tail);
    }
}

int main() {
    cin.tie(0);
    ios_base::sync_with_stdio(false);
    int n;
    cin>>n;
    for (int i=1; i<n; i++) {
        int nodeid;
        cin>>nodeid;
        appendChild(nodeid, i);
    }
    dfs(0);
    output(0);
    for (int i=0; i<ans_bamptr; i++)
        cout<<ans_bamboo[i]<<" ";
    cout<<endl;
    cout<<ans_opptr<<endl;
    for (int i=0; i<ans_opptr; i++)
        cout<<ans_op[i]<<" ";
    cout<<endl;
    return 0;
}

添加新评论