题解:P8439 Altale (Fan-made FTR 7)
Register_int · · 题解
爆标做法。我说这个题其实是 sale 有没有懂的。
我们把基环树分为两类:
- 根不在环上。此时我们的决策是将根的若干个子树删去。
- 根在环上。此时有三种决策:
- 删去根连接的某个子树。
- 删去连接环且不连接根的某个子树。
- 删去环。这个操作需要删掉两条边。
注意到根在环上的三种决策中,第一种与根不在环上一致;第二种在同一个连通块中最多操作一次,因为操作了两次就不如第三种策略了。于是我们可以转化为这个问题:
给定
n 个物品,每个物品有两个价值a_i\le b_i ,花一块钱购买可以获得a_i 的价值,花两块钱则可以获得b_i 的价值。求获得k 的价值至少要花多少钱。
设
只要对这两种求出,花费
前者是好办的,直接拆成两个物品排序,贪心选一个前缀。对于后者,我们有这样一个结论:
- 只选了
a_i 的物品最多只有一个。
原因很简单,因为
- 一段前缀选了
a_i+d_i ,后面再挑一个选了a_i 。 - 一段前缀选了一个
a_i ,剩下都选了a_i+d_i 。
前者可以枚举前缀,预处理
至此我们解决了所有问题,排序因值域为
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
// d0j1a_1701 fastIO
const int MAXN = 1e6 + 10;
int head[MAXN], to[MAXN << 1], nxt[MAXN << 1], etot, d[MAXN];
inline
void add(int u, int v) {
++etot, ++d[u], to[etot] = v, nxt[etot] = head[u], head[u] = etot;
++etot, ++d[v], to[etot] = u, nxt[etot] = head[v], head[v] = etot;
}
int s[MAXN], tp, sz[MAXN], vis[MAXN], rt[MAXN];
int n, m, cnt[MAXN], a[MAXN], b[MAXN], tot, id[MAXN];
int pos[MAXN], sum[MAXN], pre[MAXN], suf[MAXN];
int main() {
io.read(n, m);
for (int i = 1, u, v; i <= n; i++) io.read(u, v), add(u, v);
for (int i = 1; i <= n; i++) sz[i] = 1;
for (int i = 1; i <= n; i++) if (d[i] == 1) s[++tp] = i;
for (int i = 1; i <= tp; i++) {
int u = s[i];
for (int _ = head[u]; _; _ = nxt[_]) {
int v = to[_];
if (d[v] > 1) { sz[v] += sz[u]; if (--d[v] == 1) s[++tp] = v; }
}
}
for (int i = 1; i <= n; i++) --d[i];
for (int i = 1; i <= n; i++) {
if (vis[i]) continue; vis[i] = rt[i] = 1, s[tp = 1] = i;
for (; tp; ) {
int u = s[tp--];
for (int _ = head[u]; _; _ = nxt[_]) {
int v = to[_];
if (!vis[v]) vis[v] = 1, s[++tp] = v;
}
}
}
for (int i = 1; i <= n; i++) vis[i] = 0;
for (int i = 1; i <= n; i++) {
if (rt[i] || vis[i]) continue;
int x = 0, y = 0, c = 0; vis[i] = 1, s[tp = 1] = i;
for (; tp; ) {
int u = s[tp--]; ++y;
for (int _ = head[u]; _; _ = nxt[_]) {
int v = to[_];
if (rt[v]) ++c;
else if (!vis[v]) vis[v] = 1, s[++tp] = v;
if (d[u] && !d[v] && !rt[v]) x = max(x, sz[v]);
}
}
if (c == 1) ++cnt[y];
else ++tot, a[tot] = x, b[tot] = y;
}
for (int i = 1; i <= tot; i++) if (a[i] * 2 >= b[i]) ++cnt[a[i]], ++cnt[b[i] - a[i]];
tp = 0;
for (int i = n; i; i--) for (; cnt[i]--; ++tp, s[tp] = s[tp - 1] + i);
for (int i = 1, j = 1; i <= n; i++) {
for (; j <= tp && s[j] < i; j++);
if (j <= tp) pos[i] = j; else pos[i] = 1e9;
}
for (int i = 1; i <= n; i++) cnt[i] = 0;
for (int i = 1; i <= tot; i++) if (a[i] * 2 < b[i]) ++cnt[b[i]];
for (int i = n; i; i--) cnt[i] += cnt[i + 1]; tp = cnt[1];
for (int i = tot; i; i--) if (a[i] * 2 < b[i]) id[cnt[b[i]]--] = i;
for (int i = 1; i <= tp; i++) sum[i] = sum[i - 1] + b[id[i]];
*pre = 1e9;
for (int i = 1; i <= tp; i++) pre[i] = min(pre[i - 1], b[id[i]] - a[id[i]]);
for (int i = tp; i; i--) suf[i] = max(suf[i + 1], a[id[i]]);
auto f = [&](int x) -> int { x = m - x; return x < 0 ? 0 : pos[x]; };
int ans = 1e9;
for (int i = 0; i <= tp; i++) {
ans = min(ans, i * 2 + f(sum[i]));
if (i < tp) {
int res = sum[i] + suf[i + 1];
if (i) res = max(res, sum[i + 1] - pre[i]);
ans = min(ans, i * 2 + 1 + f(res));
}
}
io.write(ans);
}