icpc 昆明 A.AC
反悔贪心,跟[国家集训队]种树这题相似.
假设下标从1 到 n
定义所需修改次数a[i], 表示将第i位和第i + 1位改为ac需要更改几次
如果s[i] = ‘a’, s[i + 1] = ‘c’, 则a[i] = 0;
如果s[i] = ‘a’, s[i + 1] = ‘a’, 则a[i] = 1;
如果s[i] = ‘h’, s[i + 1] = ‘x’, 则a[i] = 2;
如果用了 a[i], 那么就不能用a[i - 1]和a[i + 1]了
比如s = “xxbb”, 选择a[2]后,s[2] = ‘a’, s[3] = ‘c’, s变为了"xacb"
那么此时如果选择a[1] (对应s[1],s[2]) 或者 a[3] ( 对应s[3],s[4]) 就会与 a[2] (对应s[2], s[3])冲突。
假设a中最小的是a[i],
那么要么只选择a[i], 不选择a[i - 1] 和 a[i + 1], 要么选择 a[i - 1] 和 a[i + 1], 不选择 a[i].
解释如下:
假设方案一是:选择a[i - 1], 不选a[i], 不选a[i + 1]
假设方案二是:选择a[i],不选a[i - 1],不选a[i + 1]
显然方案二需要的更改次数不会比方案一多(因为a[i]最小), 而且不会与a[i - 2]冲突
因此方案二更优
那么题目就可以转化为一般的反悔贪心了:
选择不相邻的a[i],在它们的和不大于k的前提下,能最多选多少个a[i]
剩下的用堆维护反悔贪心就完事了可以参考这里
#include <bits/stdc++.h>
#define sz(x) ((int)x.size())
using namespace std;
using pii = pair<int, int>;
const int N = (int)5e5 + 5;
char s[N];
int n, k, a[N];
int L[N], R[N]; // 链表
int l[N], r[N];
int vis[N];
int biao[N];
int wc[N];
void shan(int p) {
vis[p] = 1;
int zuo = L[p];
int you = R[p];
R[zuo] = you;
L[you] = zuo;
}
int main() {
scanf("%d %d %s", &n, &k, s + 1);
const int inf = n * 2 + 1;
priority_queue<pii, vector<pii>, greater<pii>> xp;
for (int i = 1; i < n; ++i) {
L[i] = i - 1;
R[i] = i + 1;
l[i] = i;
r[i] = i;
int z = 0;
if (s[i] != 'a') {
++z;
}
if (s[i + 1] != 'c') {
++z;
}
a[i] = z;
xp.emplace(z, i);
}
L[0] = 0;
R[n] = n;
int ans = 0;
int cost = 0;
int mx = n / 2;
vector<pii> xiu;
while (cost <= k && ans < mx) {
while (sz(xp) && vis[xp.top().second]) {
xp.pop();
}
if (sz(xp) == 0) {
break;
}
auto tp = xp.top(); xp.pop();
cost += tp.first;
if (cost > k) {
break;
}
++ans;
int pos = tp.second;
xiu.emplace_back(l[pos], r[pos]);
int zp = L[pos];
int yp = R[pos];
if (zp != 0 && yp != n) {
a[pos] = a[zp] + a[yp] - a[pos];
l[pos] = l[zp];
r[pos] = r[yp];
xp.emplace(a[pos], pos);
shan(zp);
shan(yp);
} else if (zp == 0) {
a[pos] = inf;
shan(yp);
} else {
a[pos] = inf;
shan(zp);
}
}
sort(xiu.begin(), xiu.end());
for (auto &i : xiu) {
if (biao[i.second] == 0) {
for (int j = i.second; j >= i.first && biao[j] == 0; --j) {
biao[j] = 1;
}
}
}
for (int i = 1; i < n; ++i) {
if (biao[i] == 1) {
wc[i] = 1;
wc[i + 1] = 1;
}
}
for (int i = 1; i <= n; ++i) {
if (wc[i]) {
int ji = 0;
int j;
for (j = i; wc[j]; ++j) {
if (ji++ % 2 == 0) {
s[j] = 'a';
}
else {
s[j] = 'c';
}
}
i = j - 1;
}
}
printf("%d\n", ans);
printf("%s\n", s + 1);
return 0;
}