太弱小了,失去了力量和精神。
思路
01 分数规划 + 长链剖分优化 dp.
首先求形如 \(\frac{\sum\limits a_i}{\sum\limits b_i}\) 式子的最值,首先想到二分答案 \(ans\),令每个结点的权值为 \(a_i - ans b_i\),问题转化成求树上的最短链。
可以设 \(f[u][i]\) 表示从结点 \(u\) 向下走 \(i\) 步得到的最短链长度,显然有 \(f[u][i] = \min\limits_{v \in son(u)} f[v][i - 1] + w(u)\),其中 \(w(u) = a_u - ans b_u\)
由于问题是静态的,于是可以考虑写成树上差分的形式。令 \(s_u\) 为从根到 \(u\) 路径的 \(w\) 之和,\(f[u][i]\) 改为从根结点到结点 \(u\) 的 \(i\) 级子结点的最短链长度,显然有:
\(f[u][i] = \min\limits_{v \in son(u)} f[v][i - 1], f[u][0] = s_u\)
那么这个式子可以用长链剖分优化。
因为不可能记录下所有的状态,所以要考虑用指针的形式写。
具体的流程是:
-
遍历当前结点的深子结点,直接将状态接到当前结点的答案中
-
遍历当前结点的其他子结点,暴力合并
-
统计答案
因为每条深链只会在链首的父结点被合并一次,所以复杂度是所有深链的长度之和,即 \(O(n)\),magic!!!1
代码
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
const double eps = 1e-4;
const double inf = 1e18;
int n, m;
int len[maxn], son[maxn], fa[maxn], a[maxn], b[maxn];
double res;
double v[maxn], s[maxn], *f[maxn], *cur;
vector<int> g[maxn];
void dfs(int u, int f)
{
fa[u] = f;
for (int v : g[u])
{
if (v == f) continue;
dfs(v, u);
if (len[v] > len[son[u]]) son[u] = v;
}
len[u] = len[son[u]] + 1;
}
void dp(int u, int pre)
{
v[u] += v[pre];
if (son[u])
{
f[son[u]] = f[u] + 1;
dp(son[u], u);
}
f[u][0] = v[u];
for (int to : g[u])
{
if ((to == pre) || (to == son[u])) continue;
f[to] = cur, cur += len[to];
dp(to, u);
for (int j = 0; j < len[to]; j++)
if ((m - j - 1 >= 0) && (m - j - 1 < len[u])) res = min(res, f[u][m - j - 1] + f[to][j] - v[u] - v[pre]);
for (int j = 0; j < len[to]; j++) f[u][j + 1] = min(f[u][j + 1], f[to][j]);
}
if (m < len[u]) res = min(res, f[u][m] - v[pre]);
}
bool check(double k)
{
res = inf;
for (int i = 1; i <= n; i++) s[i] = inf;
for (int i = 1; i <= n; i++) v[i] = a[i] - k * b[i];
f[1] = cur = s, cur += len[1];
dp(1, 0);
return (res <= 0);
}
int main()
{
// freopen("P6074_1.in", "r", stdin);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
for (int i = 1, u, v; i <= n - 1; i++)
{
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
double l = 0, r = 2023.0, res = -1;
while (l <= r - eps)
{
double mid = (l + r) / 2.0;
if (check(mid)) res = mid, r = mid - eps;
else l = mid + eps;
}
if (res < 0) puts("-1");
else printf("%.2lf\n", res);
return 0;
}
标签:P6074,int,题解,路径,结点,len,son,maxn,res
From: https://www.cnblogs.com/lingspace/p/p6074.html