题目描述:
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;
}