zoukankan      html  css  js  c++  java
  • [bzoj4726][POI2017][Sabota?] (树形dp)

    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嘛。

    来自popoqqq的题解:

    1.我们可以证明,最坏情况下,叛徒一定是叶子节点。

    即,若有一个非叶节点是叛徒,且其儿子不是叛徒,而它父亲又被策反了,那么它的儿子是叛徒从而策反父亲的机率||比例显然更优

    2.我们可以得出,一人策反,全家策反,也就是一些叛徒一定在整颗子树里

    对于一个点x,设f[x]为x点不被策反需要的最小的比例

    那么初始态,即叶子节点f[叶]=1

    对于每个非叶节点,枚举儿子,在儿子中取最大的,儿子都是叛徒的比例或儿子不是叛徒的最小值来满足此点不是叛徒

    对于每个大小大于k的子树,记录答案,去最大值

    代码比较简单,用记事本手写直接交就ac了

    #include <stdio.h>
    #include <string.h>
    #define MaxN 500010
    #define MaxBuf 1<<22
    #define RG register
    #define inline __inline__ __attribute__((always_inline))
    #define Blue() ((S==T&&(T=(S=B)+fread(B,1,MaxBuf,stdin),S==T))?0:*S++)
    #define dmax(a,b) ((a)>(b)?(a):(b))
    #define dmin(a,b) ((a)<(b)?(a):(b))
    char B[MaxBuf],*S=B,*T=B;
    template<class Type>inline void Rin(RG Type &x){
        x=0;RG int c=Blue();
        for(;c<48||c>57;c=Blue())
            ;
        for(;c>47&&c<58;c=Blue())
            x=(x<<1)+(x<<3)+c-48;
    }
    bool isn_leave[MaxN];
    int n,sz[MaxN],m;
    double f[MaxN],ans;
    struct Pointer{
        int to;
        Pointer *next;
    }*fir[MaxN],mem[MaxN],*tot=mem;
    inline void link(RG int x,RG int y){
        isn_leave[x]=true;
        *++tot=(Pointer){y,fir[x]},fir[x]=tot;
    }
    void _dfs(RG int at){
        sz[at]=1;
        for(Pointer *iter=fir[at];iter;iter=iter->next)    
            _dfs(iter->to),sz[at]+=sz[iter->to];
    }
    void tree_dp(RG int at){
        if(!isn_leave[at]) {f[at]=1.000; return;}
        for(Pointer *iter=fir[at];iter;iter=iter->next){
            tree_dp(iter->to);
            f[at]=dmax(f[at],dmin(f[iter->to],(double)sz[iter->to]/(sz[at]-1)));
        }
        if(sz[at]>m)
            ans=dmax(ans,f[at]);
    }
    int main(){
        Rin(n),Rin(m);
        for(RG int i=2;i<=n;i++){
            RG int pa;
            Rin(pa);
            link(pa,i);
        }
        _dfs(1); tree_dp(1);
        printf("%.10lf
    ",ans);
        return 0;
    }
  • 相关阅读:
    课堂派题库格式转换程序
    操作系统——进程的状态与转换
    android 通用 Intent
    android上使用蓝牙设备进行语音输入
    讯飞语音听写中数字规整问题
    【Android】隐藏底部虚拟按键
    AudioEffect中文API
    为什么要在onNewIntent的时候要显示的去调用setIntent
    android蓝牙耳机下的语音(输入/识别)及按键监听
    Android如何监听蓝牙耳机的按键事件
  • 原文地址:https://www.cnblogs.com/keshuqi/p/6338123.html
Copyright © 2011-2022 走看看