BZOJ_4726_[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。


树形DP。

设f[i]为i的子树没有完全变成叛徒的最小的x值。

显然对于每个叶子节点f[i]=1,实际上是一个无限趋近于1但是大于1的数。

每棵子树叛变,需要保证子树叛变且子树大小占比例超过x,于是我们对f[y]和siz[y]/siz[x]取min,再在儿子里面取一个最大的。

最后直接在所有siz大于等于k的子树中找出f值最大的。

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 500050
typedef double du;
du f[N];
int head[N],to[N<<1],nxt[N<<1],cnt;
int n,siz[N];
inline void add(int u,int v) {
    to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
}
void dfs(int x,int y) {
    int i;
    siz[x]=1;
    int flg=0;
    for(i=head[x];i;i=nxt[i])if(to[i]!=y) {
        flg=1;
        dfs(to[i],x);
        siz[x]+=siz[to[i]];
    }
    for(i=head[x];i;i=nxt[i])if(to[i]!=y) {
        f[x]=max(f[x],min(f[to[i]],1.0*siz[to[i]]/(siz[x]-1)));
    }
    if(!flg) f[x]=1;
}
int main() {
    int k;
    scanf("%d%d",&n,&k);
    int i,x;
    for(i=2;i<=n;i++) {
        scanf("%d",&x);
        add(i,x);add(x,i);
    }
    dfs(1,0);
    du ans=0;
    for(i=1;i<=n;i++) if(siz[i]>k) ans=max(ans,f[i]);
    printf("%.9lf",ans);
}

原文地址:https://www.cnblogs.com/suika/p/8734463.html

时间: 2024-11-09 11:43:37

BZOJ_4726_[POI2017]Sabota?_树形DP的相关文章

算法提高 金属采集_树形dp

算法提高 金属采集 时间限制:1.0s   内存限制:256.0MB 问题描述 人类在火星上发现了一种新的金属!这些金属分布在一些奇怪的地方,不妨叫它节点好了.一些节点之间有道路相连,所有的节点和道路形成了一棵树.一共有 n 个节点,这些节点被编号为 1~n .人类将 k 个机器人送上了火星,目的是采集这些金属.这些机器人都被送到了一个指定的着落点, S 号节点.每个机器人在着落之后,必须沿着道路行走.当机器人到达一个节点时,它会采集这个节点蕴藏的所有金属矿.当机器人完成自己的任务之后,可以从任

BZOJ_1060_时态同步_树形DP

题意:http://www.lydsy.com/JudgeOnline/problem.php?id=1060 分析:水水的树形DP. 用儿子的最大值更新父亲,边更新边累加ans. 代码: #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define N 1000010 #define LL long long int head[N],to[N<&l

[bzoj1812][IOI2006]riv_多叉树转二叉树_树形dp

riv bzoj-1812 IOI-2006 题目大意:给定一棵n个点树,要求在上面建立k个收集站.点有点权,边有边权,整棵树的代价是每个点的点权乘以它和它的最近的祖先收集站的距离积的和. 注释:$1\le n \le 100$,$1\le k \le 50$. 想法:显然,这是一道树形dp题.状态也非常容易... ...只不过,我们好像要枚举子集... 所以,我们这里有一个黑科技:多叉树转二叉树. 我们先把它转成二叉树,然后再进行转移即可. 状态:dp[i][j][k]表示以i为根的子树中选出

[bzoj2657][Zjoi2012]旅游 journey_ 对偶图_树形dp

旅游 bzoj-2657 Zjoi-2012 题目大意:题目链接 注释:$1\le K\le 2\cdot 10^5$. 想法:这题... 感觉和上一个题的提示有些类似,就是题目生怕你不知道这是一道对偶图的题... ... 我们先把它转成对偶图.然后我们只把分割n变形的线段两侧的点之间连边,这样就是一棵树. 紧接着我们想要遍历最多的城市,其实就是找树上直径.树形dp即可. 最后,附上丑陋的代码... ... #include <iostream> #include <cstdio>

BZOJ_1864_[Zjoi2006]三色二叉树_树形DP

题意: 分析:递归建树,然后DP,从子节点转移. 注意到红色和蓝色没有区别,因为我们可以将红蓝互换而方案是相同的.这样的话我们只需要知道当前节点是否为绿色即可. 代码: 1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 using namespace std; 5 #define N 500050 6 int lson[N],rson[N],cnt,n; 7 int f[N][2],g

BZOJ_1040_[ZJOI2008]骑士_树形DP

题意: Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各 界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战火绵延五百里,在和平环境 中安逸了数百年的Z国又怎能抵挡的住Y国的军队.于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一 个真龙天子的降生,带领正义打败邪恶.骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一 些矛盾.每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己

[bzoj3696]化合物_树形dp

化合物 bzoj-3696 题目大意:给你一棵树,定义两个点i , j之间的A值是(dis[i]-dis[lca(i,j)])xor(dis[j]-dis[lca(i,j)]).对所有的k$\in$[1,n],A值等于k的点对数量. 注释:$1\le n\le 10^5$,$1\le maxdis \le 500$. 想法:说什么异或意义下的母函数,完全不会(具体数学没看完的垃圾蒟蒻).其实就是个暴力... 我们设dp[pos][i]表示以pos为根,长度为i的链的个数,然后直接转移.每次$H^

加分二叉树_解题报告_SSL1033_2003年分区联赛提高组之三_树形dp

Description 设一个n个节点的二叉树tree的中序遍历为(l,2,3,-,n),其中数字1,2,3,-,n为节点编号.每个节点都有一个分数(均为正整数),记第j个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下: subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数 若某个子树为主,规定其加分为1,叶子的加分就是叶节点本身的分数.不考虑它的空 子树. 试求一棵符合中序遍历为(1,2,

B20J_4027_[HEOI2015]兔子与樱花_树形DP

题意: 很久很久之前,森林里住着一群兔子.有一天,兔子们突然决定要去看樱花.兔子们所在森林里的樱花树很特殊.樱花树由n个树枝分叉点组成,编号从0到n-1,这n个分叉点由n-1个树枝连接,我们可以把它看成一个有根树结构,其中0号节点是根节点.这个树的每个节点上都会有一些樱花,其中第i个节点有c_i朵樱花.樱花树的每一个节点都有最大的载重m,对于每一个节点i,它的儿子节点的个数和i节点上樱花个数之和不能超过m,即son(i) + c_i <= m,其中son(i)表示i的儿子的个数,如果i为叶子节点