[JSOI2016]最佳团体
睿智题,二分一下点权变成了\(P_i-S_i\times mid\),树上背包一下求一个大小为\(m+1\)的点权大于\(0\)的联通块即可
所以我为啥写这题来着
代码
#include<bits/stdc++.h>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define eps 1e-7
#define inf 1e15
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=2503;
struct E{int v,nxt;}e[maxn];
int head[maxn],n,num,m,sum[maxn],p[maxn],s[maxn];
inline void add(int x,int y) {
e[++num].v=y;e[num].nxt=head[x];head[x]=num;
}
double dp[maxn][maxn],w[maxn];
void dfs(int x) {
sum[x]=1;dp[x][1]=w[x];
for(re int i=head[x];i;i=e[i].nxt) {
dfs(e[i].v);sum[x]+=sum[e[i].v];
for(re int j=min(sum[x],m);j;--j)
for(re int k=0;k<=min(sum[e[i].v],m)&&j-k>=0;++k)
dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[e[i].v][k]);
}
dp[x][0]=0;
}
inline void Solve(double mid) {
for(re int i=0;i<=n;i++) w[i]=p[i]-s[i]*mid;
for(re int i=0;i<=n;i++)
for(re int j=0;j<=m;j++) dp[i][j]=-inf;
dfs(0);
}
int main() {
m=read()+1,n=read();
for(re int x,i=1;i<=n;i++)
s[i]=read(),p[i]=read(),x=read(),add(x,i);
double l=0,r=4e7;
while(r-l>eps) {
double mid=(l+r)/2.0;
Solve(mid);
if(dp[0][m]>0) l=mid;else r=mid;
}
printf("%.3lf\n",l);
return 0;
}