http://cogs.pro:8080/cogs/problem/problem.php?pid=vSXNiVegV
题意:给个树,第i个点有两个权值ai和bi,现在求一条长度为m的路径,使得Σai/Σbi最小。
思路:二分答案得p,把每个点权值变成ai-p*bi,看是否存在长为一条长为m的路使总和<=0。
tag数组表示从当前位置沿最长链走到底的值,dp数组初值表示从当前位置的重儿子走到底的值(加负号),用tag[...]+dp[..]维护从当前节点往下走若干步得到的最小值(只更新dp数组
# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm>
// # include <bits/stdc++.h> using namespace std; typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N = 3e4 + , M = 6e4 + ;
const int mod = 1e9+; int n, m, A[N * ], B[N * ], head[N], nxt[M], to[M], tot = ;
inline void add(int u, int v) {
++tot; nxt[tot] = head[u]; head[u] = tot; to[tot] = v;
}
inline void adde(int u, int v) {
add(u, v), add(v, u);
} int dep[N], mxd[N], son[N], sz[N];
inline void pre_dfs(int x, int fa = ) {
dep[x] = dep[fa] + ;
mxd[x] = dep[x]; son[x] = ;
for (int i=head[x]; i; i=nxt[i]) {
if(to[i] == fa) continue;
pre_dfs(to[i], x);
if(mxd[to[i]] > mxd[x]) mxd[x] = mxd[to[i]], son[x] = to[i];
}
sz[x] = mxd[x] - dep[x];
} int pos[N], idx;
inline void pre_pos(int x, int fa = ) {
pos[x] = ++idx;
if(son[x]) pre_pos(son[x], x);
for (int i=head[x]; i; i=nxt[i])
if(to[i] != fa && to[i] != son[x]) pre_pos(to[i], x);
} double mid_check, ans;
double dp[N], tag[N];
inline void solve(int x, int fa = ) {
double *f = &dp[pos[x]], C = (double)A[x] - mid_check * B[x];
if(son[x] == ) { //leaf
f[] = ; tag[x] = C;
if(m == ) ans = min(ans, tag[x]);
return ;
}
solve(son[x], x); f[] = -tag[son[x]];
tag[x] = tag[son[x]] + C;
for (int i=head[x], y; i; i=nxt[i]) {
if(to[i] == fa || to[i] == son[x]) continue;
solve(y = to[i], x);
double *g = &dp[pos[y]];
for (int j=; j<=sz[y] && j<m; ++j)
if(m--j <= sz[x]) ans = min(ans, f[m--j] + tag[x] + g[j] + tag[y]);
for (int j=; j<=sz[y]; ++j) f[j+] = min(f[j+], g[j] + tag[y] + C - tag[x]);
}
if(m <= sz[x]) ans = min(ans, f[m] + tag[x]);
} inline bool chk(double x) {
ans = 1e18; mid_check = x;
solve();
return ans <= ;
} int main() {
freopen("cdcq_b.in", "r", stdin);
freopen("cdcq_b.out", "w", stdout);
cin >> n >> m;
for (int i=; i<=n; ++i) scanf("%d", A+i);
for (int i=; i<=n; ++i) scanf("%d", B+i);
if(m == -) {
double ans = 1e18;
for (int i=; i<=n; ++i) ans = min(ans, (double)A[i]/B[i]);
printf("%.2lf\n", ans);
return ;
}
for (int i=, u, v; i<n; ++i) {
scanf("%d%d", &u, &v);
adde(u, v);
}
--m;
pre_dfs();
pre_pos();
double l = , r = 1e11, mid;
while(r-l > 1e-) {
mid = (l+r)/2.0;
if(chk(mid)) r = mid;
else l = mid;
}
if(l > 5e10) puts("-1");
else printf("%.2lf\n", l);
return ;
}