【bzoj4726】[POI2017]Sabota? 树形dp

原文地址:http://www.cnblogs.com/GXZlegend/p/6825598.html


题目描述

某个公司有n个人, 上下级关系构成了一个有根树。其中有个人是叛徒(这个人不知道是谁)。对于一个人, 如果他
下属(直接或者间接, 不包括他自己)中叛徒占的比例超过x,那么这个人也会变成叛徒,并且他的所有下属都会变
成叛徒。你要求出一个最小的x,使得最坏情况下,叛徒的个数不会超过k。

输入

第一行包含两个正整数n,k(1<=k<=n<=500000)。
接下来n-1行,第i行包含一个正整数p[i+1],表示i+1的父亲是p[i+1](1<=p[i+1]<=i)。

输出

输出一行一个实数x,误差在10^-6以内都被认为是正确的。

样例输入

9 3
1
1
2
2
2
3
7
3

样例输出

0.6666666667


题解

坑死人的树形dp

首先易知叛徒一定为某棵子树。

设f[i]表示使f[i]不为叛徒的最小x值。

那么叶子结点的f[i]应该为1,实际上为比1多一点但无限接近1的数。

对于非叶子节点,它不为叛徒的前提为所有子树都不能使该节点叛变。

使该节点叛变需要满足2个条件:子树叛变、子树所占比例大于x。

这两个条件都满足,需要取min;同时要让i的所有子树都不满足条件,需要取max。

然后答案为所有节点数超过k的子树的f值的最大值。

#include <cstdio>
#include <cmath>
#include <algorithm>
#define eps 1e-9
using namespace std;
int head[500010] , to[500010] , next[500010] , cnt , si[500010];
double f[500010];
void add(int x , int y)
{
	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x)
{
	int i;
	si[x] = 1;
	for(i = head[x] ; i ; i = next[i]) dfs(to[i]) , si[x] += si[to[i]];
	if(si[x] == 1) f[x] = 1;
	for(i = head[x] ; i ; i = next[i]) f[x] = max(f[x] , min(f[to[i]] , (double)si[to[i]] / (si[x] - 1)));
}
int main()
{
	int n , k , x , i;
	double ans = 0.0;
	scanf("%d%d" , &n , &k);
	for(i = 2 ; i <= n ; i ++ ) scanf("%d" , &x) , add(x , i);
	dfs(1);
	for(i = 1 ; i <= n ; i ++ ) if(si[i] > k) ans = max(ans , f[i]);
	printf("%.8lf
" , ans);
	return 0;
}

 

原文地址:https://www.cnblogs.com/GXZlegend/p/6825598.html