BZOJ 4726 POI2017 Sabota? 树形DP

来源:互联网 发布:程序员必备刷题网站 编辑:程序博客网 时间:2024/05/27 20:51

题目大意:给出一棵n个点的有根树,初始某个点是叛徒,接下来如果某一时刻某个节点的子树除自己以外的节点中,叛徒的比例超过了x,那么这一整棵子树都会变成叛徒,求x的最小值使得最坏情况下叛徒数量不会超过k

老年选手复健中……

结论1.最坏情况下初始叛徒一定是一个叶节点
证明:显然如果初始叛徒不能策反它的父亲那它就不能策反任何节点了
假设初始叛徒为pp有一个儿子qp能够策反它的父亲fap(即1size[fap]1>x),那么显然1size[p]1>1size[fap]1>x,即如果把初始叛徒设为q一样能够策反fap,因此初始叛徒为q时总叛徒数不会比初始叛徒为p的时候少

结论2.最终的所有叛徒一定是某个节点为根的子树中的所有节点
由上一个结论,显然

然后我们就可以DP了。

f[p]表示将x最小设为f[p]时节点p不会被策反,DP方程:
f[leaf]=1
f[p]=maxfaq=p{min(f[q],size[q]size[p]1)}
min里面的两个,一个是q不被策反,另一个是即使q被策反,叛徒数量也不足够策反p,两项只要满足一个,就可以导致q无法策反p
答案等于maxsize[p]>k{f[p]}

时间复杂度O(n)
带log会T。

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define M 500500using 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;}
1 0
原创粉丝点击