题目大意:给出一棵
n
个点的有根树,初始某个点是叛徒,接下来如果某一时刻某个节点的子树除自己以外的节点中,叛徒的比例超过了
老年选手复健中……
结论1.最坏情况下初始叛徒一定是一个叶节点
证明:显然如果初始叛徒不能策反它的父亲那它就不能策反任何节点了
假设初始叛徒为
p
,
结论2.最终的所有叛徒一定是某个节点为根的子树中的所有节点
由上一个结论,显然
然后我们就可以DP了。
令
f[p]
表示将
x
最小设为
f[p]=maxfaq=p{min(f[q],size[q]size[p]−1)}
min里面的两个,一个是
q
不被策反,另一个是即使
答案等于
时间复杂度
O(n)
带log会T。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 500500
using namespace std;
int n,m;
struct abcd{
int to,next;
}table[M];
int head[M],tot;
int size[M];
double f[M],ans;
//f[x]表示如果让以x为根的子树不叛变,答案最小多少
void Add(int x,int y)
{
table[++tot].to=y;
table[tot].next=head[x];
head[x]=tot;
}
void DFS(int x)
{
int i;
size[x]=1;
for(i=head[x];i;i=table[i].next)
{
DFS(table[i].to);
size[x]+=size[table[i].to];
}
}
void Tree_DP(int x)
{
int i;
if(!head[x])
{
f[x]=1;
return ;
}
for(i=head[x];i;i=table[i].next)
{
Tree_DP(table[i].to);
f[x]=max(f[x],min(f[table[i].to],(double)size[table[i].to]/(size[x]-1) ));
}
if(size[x]>m)
ans=max(ans,f[x]);
}
int main()
{
cin>>n>>m;
for(int x,i=2;i<=n;i++)
{
scanf("%d",&x);
Add(x,i);
}
DFS(1);
Tree_DP(1);
printf("%.10lf\n",ans);
return 0;
}