problem
luogu-P4322
solution
假设每个人是否被招募,用 xi={0,1}x_i=\{0,1\}xi={0,1} 代替,max∑pi∗xi∑si∗xi\max\frac{\sum p_i*x_i}{\sum s_i*x_i}max∑si∗xi∑pi∗xi。
0/10/10/1 分数规划标准式子。
二分答案 ans=∑pi∗xi∑si∗xi⇒max∑xi∗(pi−ans∗si)≥0ans=\frac{\sum p_i*x_i}{\sum s_i*x_i}\Rightarrow \max\sum x_i*(p_i-ans*s_i)\ge 0ans=∑si∗xi∑pi∗xi⇒max∑xi∗(pi−ans∗si)≥0。
将 pi−ans∗sip_i-ans*s_ipi−ans∗si 重新定为 iii 候选人的价值 valival_ivali。
树形 dpdpdp 求选 kkk 个人的最大价值。
f(u,i):f(u,i):f(u,i): 在 uuu 为根的子树内选了 iii 个人的最大价值。
树形背包合并即可。f(u,i)=max{f(u,i−j)+f(v,j)}f(u,i)=\max\{f(u,i-j)+f(v,j)\}f(u,i)=max{f(u,i−j)+f(v,j)}。
因为必须 uuu 选了才能选其子树内的其余点,所以 j≤i−1j\le i-1j≤i−1 保证必选 uuu。
最后因为可能有多个候选人是被 JYYJYYJYY 看上的,所以统一他们的根为 000,判断 f(0,k+1)f(0,k+1)f(0,k+1) 即可。
code
#include <bits/stdc++.h>
using namespace std;
#define maxn 2505
int k, n, root;
vector < int > G[maxn];
int siz[maxn];
double s[maxn], p[maxn], val[maxn];
double f[maxn][maxn];void dfs( int u ) {f[u][1] = val[u]; siz[u] = 1;for( int v : G[u] ) {dfs( v );siz[u] += siz[v];for( int i = min( siz[u], k + 1 );i;i -- )for( int j = 0;j <= min( siz[v], i - 1 );j ++ )f[u][i] = max( f[u][i], f[u][i - j] + f[v][j] );}
}double check( double x ) {for( int i = 1;i <= n;i ++ ) val[i] = p[i] - x * s[i];for( int i = 0;i <= n;i ++ )for( int j = 1;j <= k + 1;j ++ )f[i][j] = -1e9;dfs( 0 ); return f[0][k + 1];
}int main() {scanf( "%d %d", &k, &n );for( int i = 1, fa;i <= n;i ++ ) {scanf( "%lf %lf %d", &s[i], &p[i], &fa );G[fa].push_back( i );}double l = 0, r = 1e4, ans = 0;while( r - l > 1e-4 ) {double mid = (l + r) / 2;if( check( mid ) >= 0 ) ans = mid, l = mid;else r = mid;}printf( "%.3f\n", ans );return 0;
}