原题
挂个链接BZOJ
Solution
分析:
这道题目显然直接01分数规划,然后树形DPcheck就好了。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<queue>
#include<algorithm>
#define ll long long
#define re register
using namespace std;
inline int gi(){
int sum=0,f=1;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return f*sum;
}
inline ll gl(){
ll sum=0,f=1;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return f*sum;
}
const double eps=1e-4;
const int maxn=3010;
int cnt,to[maxn<<1],front[maxn],nxt[maxn<<1];
void Add(int u,int v){
to[++cnt]=v;nxt[cnt]=front[u];front[u]=cnt;
}
int n,k,siz[maxn],p[maxn],s[maxn];
double dp[maxn][maxn],tmp[maxn],v[maxn];
void merge(int u,int v){
for(re int i=0;i<=k+1;i++)tmp[i]=-1e18;
for(re int i=1;i<=siz[u] && i<=k+1;i++)
for(re int j=1;j<=min(k+1-i,siz[v]);j++)
tmp[i+j]=max(tmp[i+j],dp[u][i]+dp[v][j]);
for(re int i=0;i<=k+1;i++)dp[u][i]=max(dp[u][i],tmp[i]);
}
void dfs(int u){
siz[u]=1;dp[u][1]=v[u];
for(re int i=front[u];i;i=nxt[i]){
int v=to[i];
dfs(v);merge(u,v);
siz[u]+=siz[v];
}
}
bool check(double mid){
for(re int i=1;i<=n;i++)v[i]=p[i]-s[i]*mid;
memset(siz,0,sizeof(siz));
dfs(0);
return dp[0][k+1]>=0;
}
int main(){
k=gi();n=gi();
for(re int i=1;i<=n;i++){
s[i]=gi(),p[i]=gi();int r=gi();
Add(r,i);
}
double l=0,r=1000;
while(r-l>eps){
double mid=(l+r)/2;
for(re int i=0;i<=n;i++)
for(re int j=0;j<=k+1;j++)
dp[i][j]=-1e18;
if(check(mid))l=mid;
else r=mid;
}
printf("%.3lf
",l);
return 0;
}