[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;
}

posted @ 2019-09-24 10:20  asuldb  阅读(111)  评论(0编辑  收藏  举报