【BZOJ4753】最佳团体(JSOI2016)-01分数规划+树形DP

测试地址:最佳团体
做法:本题需要用到01分数规划+树形DP。
首先看到比值,想到01分数规划,按照套路移项后,我们发现这就是一个点权为pksk为二分的比值)的一个树上背包,直接做就行了,时间复杂度是O(n2logans)
话说我居然忘了树上背包复杂度O(n2)的证明……这里简单写一下。我们每次用某棵子树的答案更新根时,前面涉及到的背包的大小不超过前面所有子树的点数和,再乘上当前子树的点数和,我们发现这就是有一点在当前子树中,另一点在其它子树中,且LCA为当前根的点对数目。因为每个点对只会在LCA处被算一次,因此树上背包的时间复杂度是O(n2)的。
(BZOJ上都能过的题,居然在洛谷上被卡时了,差评)
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
const double eps=1e-5;
const double inf=1e9;
int k,n,first[2510]={0},siz[2510],tot=0;
double maxp,s[2510],p[2510],f[2510][2510],val[2510];
struct edge
{
    int v,next;
}e[2510];

void insert(int a,int b)
{
    e[++tot].v=b;
    e[tot].next=first[a];
    first[a]=tot;
}

void dp(int v)
{
    siz[v]=1;
    f[v][0]=0.0,f[v][1]=val[v];
    for(int i=2;i<=n+1;i++) f[v][i]=-inf;
    for(int i=first[v];i;i=e[i].next)
    {
        dp(e[i].v);
        for(int j=siz[v];j>=1;j--)
            for(int k=1;k<=siz[e[i].v];k++)
                f[v][j+k]=max(f[v][j+k],f[v][j]+f[e[i].v][k]);
        siz[v]+=siz[e[i].v];
    }
}

int main()
{
    scanf("%d%d",&k,&n);
    for(int i=1;i<=n;i++)
    {
        int r;
        scanf("%lf%lf%d",&s[i],&p[i],&r);
        insert(r,i);
        maxp=max(maxp,p[i]);
    }

    val[0]=0.0;
    double l=0.0,r=maxp;
    while(r-l>=eps)
    {
        double mid=(l+r)/2.0;
        for(int i=1;i<=n;i++)
            val[i]=p[i]-mid*s[i];
        dp(0);
        if (f[0][k+1]>eps) l=mid;
        else r=mid;
    }
    printf("%.3lf",l);

    return 0;
}
posted @ 2018-04-25 21:00  Maxwei_wzj  阅读(97)  评论(0编辑  收藏  举报