2015-2016 ACM ICPC Baltic Selection Contest C Minimax Tree


题目描述:

http://codeforces.com/gym/100796/problem/C

题解:

很好的一道题目.
正着做是树形dp,显然n太大.
我们习惯性的反着考虑,看一个值能不能成为最小值.
那么最小的那个只需要到根的距离的min就能够保证(1).
那次小的呢?就是在和比它大的汇合的时候一定要取一次min,
那么我们搞lca,在这里取min一定是最好的. 那么发现(1)也不准确了,
如果最小值到根的一个节点中没有比它大的值和它汇合,那就不用这一层了.
所以就是对于i的min需求,看所有的比它大的lca,去掉重复之后数个数
我们可以从小的i开始递推,用线段树将整棵字数减-

重点:

1.倒着想,看值能不能行.
2.一个一个的看,一对一对的比较. 得到lca的性质.

代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>

using namespace std;

const int maxn = 5e5 + 100;

int n, minN, k;
vector<int> G[maxn];
int tree[maxn], no[maxn], fanno[maxn], cnt, first[maxn], last[maxn];
int deep[maxn], son[maxn], pa[maxn];

struct node {
    int val, id;
    node(int _val = 0, int _id = 0) {
        val = _val;
        id = _id;
    }
};
node a[maxn];
int an;
int cmp(node a, node b) {
    return a.val < b.val;
}

void pushDown(int rt) {
    int lrt = 2 * rt, rrt = lrt + 1;
    if(tree[rt] == 0)
        return;
    tree[lrt] += tree[rt];
    tree[rrt] += tree[rt];
    tree[rt] = 0;
}
void initail(int rt, int l, int r) {
    if(l == r) {
        tree[rt] = deep[fanno[l]];
        return;
    }
    int lrt = 2 * rt, rrt = 2 * rt + 1, mid = (l + r) / 2;
    tree[rt] = 0;
    initail(lrt, l, mid);
    initail(rrt, mid + 1, r);
}
void update(int L, int R, int k, int rt, int l, int r) {
    //printf("555  %d  %d  %d\n", rt, l, r);
    if(L <= l && R >= r) {
        tree[rt] += k;
        return;
    }
    pushDown(rt);
    int lrt = rt * 2, rrt = rt * 2 + 1, mid = (l + r) / 2;
    if(L <= mid)
        update(L, R, k, lrt, l, mid);
    if(R >= mid + 1)
        update(L, R, k, rrt, mid + 1, r);
}
int query(int pos, int rt, int l, int r) {
    if(l == r) {
        return tree[rt];
    }
    int lrt = 2 * rt, rrt = 2 * rt + 1, mid = (l + r) / 2;
    pushDown(rt);
    if(pos <= mid)
        return query(pos, lrt, l, mid);
    return query(pos, rrt, mid + 1, r);
}
void dfs1(int u, int fa) {
    son[u] = 0;
    cnt++;
    no[u] = cnt;
    fanno[cnt] = u;
    first[u] = cnt;
    for(int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if(v != fa) {
            pa[v] = u;
            son[u]++;
            deep[v] = deep[u] + 1;
            dfs1(v, u);
        }
    }
    last[u] = cnt;
}
void sub(int u) {
    if(u == 0)
        return;
    son[u]--;
    //printf("444  %d %d %d %d\n", first[u], last[u], no[6], query(no[6], 1, 1, cnt));
    if(son[u] == 1) {
        // printf("222  %d  %d  %d\n", u, fanno[first[u]], fanno[last[u]]);
        update(first[u], last[u], -1, 1, 1, cnt);
    }
    //printf("444  %d %d %d %d\n", first[u], last[u], no[6], query(no[6], 1, 1, cnt));
    if(son[u] == 0) {
        sub(pa[u]);
    }
}
int dfs2(int u) {
    son[u] = 0;
    for(int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if(v != pa[u]) {
            son[u]++;
            dfs2(v);
        }
    }
}
int cmp2(node a, node b) {
    return a.val > b.val;
}

void solve() {
    deep[1] = 0;
    pa[1] = 0;
    cnt = 0;
    dfs1(1, 0);
    initail(1, 1, cnt);
    for(int i = 1; i <= n; i++) {
        if(son[i] == 1) {
            update(first[i], last[i], -1, 1, 1, cnt);
        }
    }
    minN = k;
    int ansMin;
    sort(a, a + an, cmp);
    for(int i = 0; i < an; i++) {
        int t = query(no[a[i].id], 1, 1, cnt);
        if(t <= minN) {
            ansMin = a[i].val;
            break;
        }
        //printf("33 %d\n", pa[a[i].id]);
        sub(pa[a[i].id]);
    }
    printf("%d ", ansMin);
    sort(a, a + an, cmp2);
    int maxMax = n - an - minN;
    dfs2(1);
    initail(1, 1, cnt);
     for(int i = 1; i <= n; i++) {
        if(son[i] == 1) {
            update(first[i], last[i], -1, 1, 1, cnt);
        }
    }
    int ansMax;
    for(int i = 0; i < an; i++) {
        int t = query(no[a[i].id], 1, 1, cnt);
        if(t <= maxMax) {
            ansMax = a[i].val;
            break;
        }
        sub(pa[a[i].id]);
    }
    printf("%d\n", ansMax);
}

int main() {
    //freopen("c.txt", "r", stdin);
    while(scanf("%d%d", &n, &k) != EOF) {
        for(int i = 1; i <= n; i++)
            G[i].clear();
        for(int i = 2; i <= n; i++) {
            int t;
            scanf("%d", &t);
            G[t].push_back(i);
        }
        an = 0;
        for(int i =1; i <= n; i++) {
            int t;
            scanf("%d", &t);
            if(t != 0) {
                a[an] = node(t, i);
                an++;
            }
        }
        solve();
    }
    return 0;   
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值