2023 ICPC Nanjing
2023 ICPC Nanjing
ICPC Nanjing
G
可以选 \(k\) 个物品免费的 01 背包问题。考虑我买了 \(x\) 个物品,现在可以使一些物品免费,最优该如何选。肯定是从价格最高的物品开始选,这样可以留下更多的钱。也就是说对于最终的状态,我所花原价买的物品的价格,一定是要比我选则免费的物品的价格要少的。所以我们对所有物品按照价格升序排序后 dp,当 dp 完第 i 个物品时,我们令其为我们选择原价购买的最后一个物品,其他的物品都从后面选价值最大的 \(k\) 个。
void solve() {
int n = 0, W = 0, k = 0;
std::cin >> n >> W >> k;
std::vector p(n, std::pair<int, i64>{});
for (auto &[w, v] : p) {
std::cin >> w >> v;
}
std::sort(p.begin(), p.end());
std::vector suf(n + 1, 0ll);
std::priority_queue<i64, std::vector<i64>, std::greater<i64>> q;
for (int i = 1; i <= k; ++i) {
q.push(p[n - i].second);
suf[n - k] += p[n - i].second;
}
for (int i = n - k - 1; i >= 0; --i) {
suf[i] = suf[i + 1];
if (not q.empty() && p[i].second > q.top()) {
suf[i] -= q.top();
q.pop();
suf[i] += p[i].second;
q.push(p[i].second);
}
}
i64 ans = 0;
std::vector f(W + 1, 0ll);
for (int i = 0; i < n; ++i) {
for (int j = W; j >= p[i].first; --j) {
f[j] = std::max(f[j], f[j - p[i].first] + p[i].second);
}
ans = std::max(ans, f[W] + suf[i + 1]);
}
std::cout << std::max(ans, suf[0]) << '\n';
return;
}
A
我们一个一个连通块地考虑,如果一个连通块不是除自己外的任意一个连通块的子图(包括完全相等的情况),我们就说这个连通块中的点都是有可能留到最后的,否则就不是。
实现方面,枚举每个连通块,枚举这个连通块的起点坐标来进行匹配,如果一次枚举过程中,该连通块中的点没出出局的情况(掉在洞里或越界)我们就认为它被某个连通块包含,是一个连通块的子图。
M
首先拆掉 min。有
\[min(f_i, g_i) = f_i + g_i - max_{j = 1}^{n}(a_j)
\]
因为对于任意的 \(i\),\(f_i\) \(g_i\) 中必有一个是 \(max_{j = 1}^{n}(a_j)\),取最小值就是两者之和减去这个最大值。
于是我们就可以单独地维护 \(f_i\)、\(g_i\) 和 \(a_i\) 的和,对于 \(f_i\) 可以设计以下类来维护插入值的操作和求和的操作
// 用 set 维护单调栈
class set {
public:
set(int _n) {
n = _n;
s.clear();
// 最开始插入一个最小的数
// 最后插入一个最大的数
s.insert({ 0, 0ll });
s.insert({ n + 1, Inf });
sum = 0ll;
}
void insert(int pos, i64 val) {
s.insert({ pos, val });
auto it = s.find({ pos, val });
auto lst = std::prev(it);
auto nxt = std::next(it);
// 上一个元素大于等于插入的元素就直接不插入
if ((*lst).second >= (*it).second) {
s.erase(it);
return;
}
sum -= len(*lst, *nxt);
sum += len(*it, *nxt);
sum += len(*lst, *it);
if ((*it).first == (*lst).first) {
s.erase(lst);
}
while ((*nxt).second <= (*it).second) {
auto tmp = std::next(nxt);
sum -= len(*it, *nxt);
sum -= len(*nxt, *tmp);
sum += len(*it, *tmp);
s.erase(nxt);
nxt = tmp;
}
}
i64 get() {
return sum;
}
private:
int n;
std::set<std::pair<int, i64>> s;
i64 sum;
i64 len(std::pair<int, i64> l, std::pair<int, i64> r) {
return 1ll * (r.first - l.first) * l.second;
}
};
浙公网安备 33010602011771号