找点概率期望的题做一做
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
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