【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\n" , ans); return 0; }