BZOJ 4726 POI2017 Sabota? 树形DP

50 篇文章 1 订阅
25 篇文章 1 订阅

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

老年选手复健中……

结论1.最坏情况下初始叛徒一定是一个叶节点
证明:显然如果初始叛徒不能策反它的父亲那它就不能策反任何节点了
假设初始叛徒为 p p有一个儿子 q p能够策反它的父亲 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 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;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值