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