BZOJ 4726 POI 2017 Sabota? 树形DP

4726: [POI2017]Sabota?

Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special Judge
Submit: 128  Solved: 49
[Submit][Status][Discuss]

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。

Source

鸣谢Claris上传

Solution

考虑不满足条件的情况,肯定是叶节点中出现了叛徒,然后不断往上传染。

考虑树形DP,f[i]为i节点不变成叛徒的最小的比例,当i为叶节点时,f[i] = 1.0。

转移:f[i] = max{f[i], min(f[son[i]], 1.0*siz[son[i]]/(son[i]-1))};

其儿子不变成叛徒的最小比例为x,小于x,则其儿子会变成叛徒;其儿子所占的比例为y,小于y,则其儿子变成叛徒后会影响到它。

故取p = min(x,y),小于p,则它会变成叛徒。取max{p},是为了让它的所有儿子都不影响到它。

答案就是所有子树大小大于k的f[i]的max值。

且需要注意,一开始只有一个叛徒,也就是说传染最多只会传染完某一棵子树。

Code

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 const int maxn = 5e5+10;
 6 const double EPS = 1e-8;
 7 #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
 8 #define REP_EDGE(i, a) for (int i = (a); i != -1; i = e[i].nxt)
 9 #define mset(a, b) memset(a, b, sizeof(a))
10 int fcmp(double x) { if (x > -EPS && x < EPS) return 0; return x < -EPS ? -1 : 1; }
11 double max_(const double &AI, const double &BI) { return fcmp(AI-BI) == 1 ? AI : BI; }
12 double min_(const double &AI, const double &BI) { return fcmp(AI-BI) == -1 ? AI : BI; }
13 int n, k;
14 struct Edge
15 {
16     int v, nxt;
17     Edge (int v = 0, int nxt = 0): v(v), nxt(nxt) {}
18 }e[maxn];
19 int head[maxn], label, siz[maxn];
20 double f[maxn];
21 
22 void ins(int u, int v) { e[++label] = Edge(v, head[u]), head[u] = label; }
23 
24 void dfs(int u, int fa)
25 {
26     siz[u] = 1;
27     REP_EDGE(i, head[u])
28         if (e[i].v != fa)
29             dfs(e[i].v, u), siz[u] += siz[e[i].v];
30     if (siz[u] == 1) { f[u] = 1.0; return ; }
31     f[u] = 0.0;
32     REP_EDGE(i, head[u])
33         if (e[i].v != fa)
34             f[u] = max_(f[u], min_(1.0*siz[e[i].v]/double(siz[u]-1), f[e[i].v]));
35 }
36 
37 int main()
38 {
39     scanf("%d %d", &n, &k);
40     REP(i, 1, n) head[i] = -1;
41     label = -1;
42     REP(i, 2, n){ int u; scanf("%d", &u), ins(u, i); }
43     dfs(1, 0);
44     double ans = 0.0;
45     REP(i, 1, n)
46         if (siz[i] > k) ans = max_(ans, f[i]);
47     printf("%.8lf\n", ans);
48     return 0;
49 }
View Code

 

posted @ 2017-03-21 15:00  Splay  阅读(218)  评论(0编辑  收藏  举报