bzoj 4753: [Jsoi2016]最佳团体

Description

JSOI信息学代表队一共有N名候选人,这些候选人从1到N编号。方便起见,JYY的编号是0号。每个候选人都由一位
编号比他小的候选人Ri推荐。如果Ri=0则说明这个候选人是JYY自己看上的。为了保证团队的和谐,JYY需要保证,
如果招募了候选人i,那么候选人Ri"也一定需要在团队中。当然了,JYY自己总是在团队里的。每一个候选人都有
一个战斗值Pi",也有一个招募费用Si"。JYY希望招募K个候选人(JYY自己不算),组成一个性价比最高的团队。
也就是,这K个被JYY选择的候选人的总战斗值与总招募总费用的比值最大。

solution

正解:分数规划+树上背包
首先看到比值最大,可以考虑二分答案
分数规划问题本质是 存在答案 \(x=\sum{pi}/\sum{si}>=x'\)
那么\(\sum{pi}>=x'*\sum{si}\)
\(\sum{pi}-x'*\sum{si}>=0\)
我们把点权改为 \(pi-x'*si\) 如果能得出\(sum==0\)此时的 \(x=x'\) 即为最后答案
对于check,就是一个裸的树上背包,复杂度可以证明是 \(O(n^2)\)

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N=2505,inf=2e8;const double eps=1e-5;
int K,n,num=0,head[N],to[N],nxt[N],w[N],v[N],sz[N];double val[N];
void link(int x,int y){nxt[++num]=head[x];to[num]=y;head[x]=num;}
double f[N][N];
il void dfs(int x){
   RG int u,j,k;sz[x]=1;f[x][1]=val[x];
   for(int i=head[x];i;i=nxt[i]){
      u=to[i];dfs(u);
      for(j=sz[x];j>=1;j--){
         for(k=sz[u];k>=1;k--){
            f[x][j+k]=Max(f[x][j+k],f[x][j]+f[u][k]);
         }
      }
      sz[x]+=sz[u];
   }
}
bool check(double mid){
   RG int i,j;
   for(i=0;i<=n;i++)
      for(j=0;j<=n+3;j++)
         f[i][j]=-inf;
   for(i=1;i<=n;i++)val[i]=1.0*v[i]-w[i]*mid;
   dfs(0);
   return f[0][K+1]>eps;
}
void work()
{
   int x;
   scanf("%d%d",&K,&n);
   for(int i=1;i<=n;i++){
      scanf("%d%d%d",&w[i],&v[i],&x);
      link(x,i);
   }
   double l=0,r=10000,mid,ans=0;
   while(l<=r-eps){
      mid=(l+r)/2;
      if(check(mid))ans=mid,l=mid+eps;
      else r=mid-eps;
   }
   printf("%.3lf\n",ans);
}
 
int main()
{
    work();
    return 0;
}
posted @ 2017-10-21 00:01  Hxymmm  阅读(191)  评论(0编辑  收藏  举报