[JZOJ4512][JSOI2016]最佳团队

来源:互联网 发布:淘宝看数据的插件 编辑:程序博客网 时间:2024/06/12 01:37

题目大意

一棵树,有n+1个节点,根编号为0
每个非根节点都有两个权值sipi,父亲ri
要求选择K+1个节点,最大化

pisi

并且所选节点一定包括根,并且如果选择了节点x(x0)那么x的父亲ri一定要选。

1Kn2500,0<si,pi104,0ri<i


题目分析

看见最大化的分式,显然要使用01分数规划。
二分答案转化为判定性问题,计算是否存在权值和小于零的选择方案。
由于这里有树形依赖关系,那我们需要用一个神奇的方法做树形dp
注意到x的子树中任一节点能选择当且仅当x被选择。
所以我们可以按照DFS序列来dp,令fi,j表示待处理节点在DFS序中排名为i,已经选择了j个节点,令其节点编号为p
如果选择了p,那么其子树就可以考虑,转移到fi+1,j+1
如果不选择p,那么就要跳过其子树,转移到fi+sizep,j
这个dp很巧妙,利用了树形依赖的性质。
时间复杂度O(nKlog2(ns))


代码实现

#include <iostream>#include <climits>#include <cfloat>#include <cstdio>#include <cctype>using namespace std;typedef double db;int read(){    int x=0,f=1;    char ch=getchar();    while (!isdigit(ch)) f=(ch=='-')?-1:f,ch=getchar();    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();    return x*f;}const db INF=DBL_MAX/3;const db EPS=0.0001;const int N=2550;const int K=2550;int fa[N],last[N],tov[N],next[N],s[N],p[N],size[N],id[N];int n,k,tot,idx,sum;db f[N][K],v[N];db ans;void insert(int x,int y){    tov[++tot]=y,next[tot]=last[x],last[x]=tot;}void dfs(int x){    size[id[++idx]=x]=1;    for (int i=last[x],y;i;i=next[i])        dfs(y=tov[i]),size[x]+=size[y];}bool check(db now){    for (int i=2;i<=n+1;i++)    {        v[i]=s[id[i]]*now-p[id[i]];        for (int j=0;j<=k;j++)            f[i][j]=INF;    }    f[2][0]=0;    for (int i=2;i<=n;i++)    {        for (int j=0;j<=k&&j<=i-2;j++)        {            if (f[i][j]<f[i+size[id[i]]][j]) f[i+size[id[i]]][j]=f[i][j];            if (j<k&&f[i+1][j+1]>f[i][j]+v[i]) f[i+1][j+1]=f[i][j]+v[i];        }    }    return f[n+1][k]>0;}double binary_search(){    db l=0,r=sum,mid,ret;    while (l+EPS<r)    {        mid=(l+r)/2;        if (check(mid)) r=mid;        else ret=mid,l=mid;    }    return ret;}int main(){    freopen("team.in","r",stdin),freopen("team.out","w",stdout);    k=read(),n=read();    for (int i=1;i<=n;i++)    {        s[i+1]=read(),sum+=(p[i+1]=read()),fa[i+1]=read();        insert(++fa[i+1],i+1);    }    n++;    dfs(1);    ans=binary_search();    printf("%.3lf\n",ans);    fclose(stdin),fclose(stdout);    return 0;}
0 0