BZOJ 4726 POI2017 Sabota? 樹形DP

題目大意:給出一棵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 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章