A - Prison Break
四个角贪心
int main() {
IOS;
for (cin >> _; _; --_) {
int a, b, x, y; cin >> a >> b >> x >> y;
int ans = max(a - x + b - y, a - x + y - 1);
umax(ans, x + y - 2); umax(ans, x - 1 + b - y);
cout << ans << '
';
}
return 0;
}
B - Repainting Street
又是分段模拟
int main() {
IOS;
for (cin >> _; _; --_) {
cin >> n >> k;
int ls = 0, ans = 1e9;
unordered_map<int, vector<PII>> st;
rep (i, 1, n) {
cin >> m;
if (ls != m) st[m].pb({ i, i });
else ++st[m].back().se;
ls = m;
}
for (auto &i : st) {
int res = 0, ls = 0, c;
if (i.se[0].fi != 1) {
c = i.se[0].fi - 1;
res = (c - 1) / k + 1; ls = c % k ? k - c % k : 0;
}
rep (j, 1, i.se.size() - 1) {
ls = max(0, ls - i.se[j - 1].se + i.se[j - 1].fi - 1);
c = i.se[j].fi - i.se[j - 1].se - 1 - ls;
if (c > 0) res += (c - 1) / k + 1, ls = c % k ? k - c % k : 0;
else ls = -c;
}
if (i.se.back().se != n) {
ls = max(0, ls - i.se.back().se + i.se.back().fi - 1);
c = n - i.se.back().se - ls;
if (c > 0) res += (c - 1) / k + 1;
}
umin(ans, res);
}
cout << ans << '
';
}
return 0;
}
C - Bouncing Ball
dp, f[i] 表示从 i 开始弹, 弹出游戏最大长度所需的花费
ll f[N];
char s[N];
int main() {
IOS;
for (cin >> _; _; --_) {
ll n, k, p, x, y; cin >> n >> p >> k >> s + 1 >> x >> y;
per (i, n, 1) f[i] = (i + k > n ? 0 : f[i + k]) + (s[i] ^ '0' ? 0 : x);
ll ans = 2e18;
rep (i, 1, n + 1 - p) umin(ans, f[i + p - 1] + (i - 1ll) * y);
cout << ans << '
';
}
return 0;
}
D - XOR-gun
XOR, 肯定往二进制想, 思考得到我们要找个的位置 i
使得 [l, i] > [i + 1, r] 其中 i - l + r - i - 1 最小, [l, r] 表示合并 l~r 区间
明显会超时, (n^3), 但是注意到,
两个相邻的数二进制最高位相同则会得到一个比原先小的数, 如果其两边存在一个数最高位跟这两个数相同, 答案直接是 1
通过二进制只有31位, 排除复杂情况, 降低复杂度
将这种情况排除, 则 n就会缩小到 31 * 2, (n^3) 也无所谓
这道题让我想起了 这场的E, 为什么我的线段树不会超时呢?
懂了这道题, 应该能想明白的
int main() {
IOS; cin >> n; m = n;
rep (i, 1, n) {
cin >> a[i]; ++b[__builtin_clz(a[i])];
a[i] ^= a[i - 1];
}
rep (i, 0, 31) if (b[i] > 2) { cout << 1; return 0; }
rep (l, 1, n - 1) rep (r, l + 1, n) rep (k, l, r - 1)
if ((a[k] ^ a[l - 1]) > (a[r] ^ a[k])) umin(m, r - l - 1);
cout << (m == n ? -1 : m);
return 0;
}
E - New Game Plus!
每个boss_i的贡献为, (c_i * (n - i)), 明显贪心的, 要从大到小排序, 直接扫一遍就行
对于 k > 0, 即清空所有的 赏金, 相当于重新开始游戏
即 你可以将 n 个boss 最多分成 k + 1 个游戏, 使得分最高
当然对于一个游戏, boss 越多, 前几个boss 的贡献就越大, 即设当前 赏金为 x
则向 这个游戏添加一个 (boss_j), 则获得积分为 x * ((curNumberOfBoss)), 赏金变为 (x += c_j)
这不是明显吗, 已经贪过心, 每次将 (boss_i) 添加到任何一个游戏(k + 1)的末尾, 使得 x 最大即可, 那不就是优先队列吗?
妙啊
int main() {
IOS; cin >> n >> m;
rep (i, 1, n) cin >> a[i];
sort(a + 1, a + 1 + n, greater<int>());
ll ans = 0; priority_queue<ll> q(m + 1, 0);
rep (i, 1, n) {
ll c = q.top(); q.pop();
q.push(c + a[i]); ans += c;
}
cout << ans;
return 0;
}