【树形dp】bzoj4726: [POI2017]Sabota?

找点概率期望的题做一做

Description

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

Input

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

Output

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

Sample Input

9 3
1
1
2
2
2
3
7
3

Sample Output

0.6666666667

HINT

答案中的x实际上是一个无限趋近于2/3但是小于2/3的数

因为当x取2/3时,最坏情况下3,7,8,9都是叛徒,超过了k=3。

题目分析

第一眼肯定是二分想法。但是好像不是很会贪心check。

于是干脆考虑dp,从问题反面入手。先丢开叛徒总数的限制,$f[i]$表示$i$这整颗子树没叛变的最小x值。转移就是$f[i]=max{f[i],min{size_j/(size_i-1),f[j]}}$.因为子树$i$叛变的情况需要同时满足:一是子树$j$占比例超过$x$,二是子树$j$也叛变了。那么,最小的$f[i]$只会出现在它们两个的最小值中。又由于所有情况下都要保证子树$i$不叛变,所以$f[i]$这里是取max的。最后统计答案时,就是所有子树$size>k$的最大$f[i]$。

看题还是不仔细,第一次看成“叛徒个数小于k"WA了一发。

 1 #include<bits/stdc++.h>
 2 const int eps = 1e-10;
 3 const int maxn = 500035;
 4 
 5 int n,k,tot[maxn];
 6 double ans,f[maxn];
 7 std::vector<int> a[maxn];
 8 
 9 int read()
10 {
11     char ch = getchar();
12     int num = 0;
13     bool fl = 0;
14     for (; !isdigit(ch); ch = getchar())
15         if (ch=='-') fl = 1;
16     for (; isdigit(ch); ch = getchar())
17         num = (num<<1)+(num<<3)+ch-48;
18     if (fl) num = -num;
19     return num;
20 }
21 void dfs(int x)
22 {
23     tot[x] = 1;
24     for (int i=0; i<a[x].size(); i++)
25         dfs(a[x][i]), tot[x] += tot[a[x][i]];
26     if (tot[x]==1) f[x] = 1;
27     else{
28         for (int i=0; i<a[x].size(); i++)
29             f[x] = std::max(f[x], std::min(f[a[x][i]], 1.0*tot[a[x][i]]/(tot[x]-1)));
30     }
31 }
32 int main()
33 {
34     n = read(), k = read();
35     for (int i=2; i<=n; i++) a[read()].push_back(i);
36     dfs(1);
37     for (int i=1; i<=n; i++)
38         if (tot[i] > k) ans = std::max(ans, f[i]);
39     printf("%.8lf
",ans);
40     return 0;
41 }

END

原文地址:https://www.cnblogs.com/antiquality/p/9820984.html