icpc 昆明 A.AC

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;
}









  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值