bzoj 4726 [POI2017]Sabota? 树形dp
[POI2017]Sabota?
Time Limit: 20 Sec Memory Limit: 128 MBSec Special JudgeSubmit: 741 Solved: 314
[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
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。
并且带头叛变的人一定是从某个叶子往上走一条链
因为如果i没有带头叛变,那么i的父亲也一定不会带头叛变,证明显然
f[i]表示i不带头叛变的话最小的x
那么我们对所有子树大小>k的f值取max即是答案
f[i]=max j为i的儿子 (min(f[j],siz[j]/(siz[i]-1))
因为对于i的一个儿子j,假如i因为j的子树里的叛徒比例大于x而带头叛变,那么既要满足x<=(j的子树大小占i的子树大小的比例),还要满足j带头叛变即x<=f[j],所以对两个量取min
那么如果i不叛变,那么就不能满足任意一个条件,所以对所有的取max
对于叶子,f[i]=1,因为不管怎样叶子本身就是叛徒,可以视为不需要条件就可以带头叛变,即只有当x>1时才不会叛变
1 #include<cstring> 2 #include<cstdio> 3 #include<algorithm> 4 #include<iostream> 5 #include<cmath> 6 #include<map> 7 8 #define N 500007 9 #define ll long long 10 using namespace std; 11 inline ll read() 12 { 13 ll x=0,f=1;char ch=getchar(); 14 while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} 15 while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} 16 return x*f; 17 } 18 19 int n,k; 20 int cnt,hed[N],rea[N<<1],nxt[N<<1]; 21 int siz[N]; 22 double f[N],ans; 23 24 void add(int u,int v) 25 { 26 nxt[++cnt]=hed[u]; 27 hed[u]=cnt; 28 rea[cnt]=v; 29 } 30 void dfs(int u) 31 { 32 siz[u]=1; 33 for (int i=hed[u];i!=-1;i=nxt[i]) 34 { 35 int v=rea[i]; 36 dfs(v); 37 siz[u]+=siz[v]; 38 } 39 if (hed[u]==-1) f[u]=1; 40 else 41 { 42 for (int i=hed[u];i!=-1;i=nxt[i]) 43 { 44 int v=rea[i]; 45 f[u]=max(f[u],min(1.0*siz[v]/(siz[u]-1),f[v])); 46 } 47 } 48 if (siz[u]>k) ans=max(ans,f[u]); 49 } 50 int main() 51 { 52 memset(hed,-1,sizeof(hed)); 53 n=read(),k=read(); 54 for (int i=2;i<=n;i++) 55 add(read(),i); 56 dfs(1); 57 printf("%.8lf\n",ans); 58 }