2026ICPC深圳邀请赛 D. City Management
对每次加入工人进行分段处理,同时维护工厂的前 k 大和。
每次处理当前工人,记当前工人和下一工人之间的工厂为新段,计算新段中每个工厂的贡献,然后将其降序排序,最后枚举分配去新工厂的人数和去旧工厂的人数,统计共同的贡献,取最大值就做完了。
这里维护前 k 大值的和用到了对顶堆,详细见代码。
#include <bits/stdc++.h>
// Eternal vigilance.
// from: cpp.json
#define INF 8e18
#define LD long double
#define i128 __int128_t
#define int long long
using namespace std;
void solve() {
int n;
cin >> n;
priority_queue<int, vector<int>, greater<int>> minpq;
priority_queue<int> maxpq;
// count: 旧工厂数量
int sum = 0, count = 0;
auto adjust = [&](int k) -> void {
while (minpq.size() < k && !maxpq.empty()) {
int val = maxpq.top();
maxpq.pop();
minpq.push(val);
sum += val;
}
while (minpq.size() > k) {
int val = minpq.top();
minpq.pop();
maxpq.push(val);
sum -= val;
}
};
auto getSumK = [&](int k) -> int {
adjust(k);
return sum;
};
auto insert = [&](int x) -> void {
count++;
if (!minpq.empty() && x > minpq.top()) {
minpq.push(x);
sum += x;
} else {
maxpq.push(x);
}
};
vector<pair<char, int>> ops(n + 1);
vector<int> days;
for (int i = 1; i <= n; i++) {
char op;
cin >> op;
ops[i].first = op;
if (op == 'F') {
cin >> ops[i].second;
} else {
days.push_back(i);
}
}
days.push_back(n + 1);
// K: 当前员工
int ans = 0, K = 0, lst = 1;
for (int d : days) {
if (lst < d) {
int L = lst;
int R = d - 1;
int len = R - L + 1;
vector<int> ws;
for (int j = L; j <= R; j++) {
ws.push_back(ops[j].second * (R - j + 1));
}
sort(ws.rbegin(), ws.rend());
int maxp = 0;
int cur = 0;
int limit = min((int) ws.size(), K);
// j 个人去新工厂, min(K - j, count) 个人去旧工厂
for (int j = 0; j <= limit; j++) {
if (j > 0) {
cur += ws[j - 1];
}
int old = min(K - j, count);
int os = getSumK(old);
// 旧工厂 + 新工厂
maxp = max(maxp, cur + os * len);
}
ans += maxp;
// 插入旧工厂
for (int j = L; j <= R; j++) {
insert(ops[j].second);
}
}
if (d <= n) {
K++;
lst = d;
}
}
cout << ans << "\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}

浙公网安备 33010602011771号