模拟

提供一种另类的模拟算法,适用于更多的情况(而不一定为 式,虽然赛后才发现最多只会删除两次)。

如果一次操作没有删除任何数的话,那么这次操作是毫无意义的,因此操作最多执行 次。为了快速删除一个数,我们可以使用链表的结构。那么这样子的时间复杂度是 的,仍然不优。

仔细观察,删除第 个字符只会影响第 个字符和第 个字符,因此删除了之后直接对这两个进行判定就行了。但是我们需要考虑复杂一点的情况,假如有若干个需要删除的连续字符,那么我们只删除了一个就去对前一个字符作判定是不对的,因为后面的字符还没有删掉。

考虑新加一个 wait 数组,我们先把需要删的删掉了再来判断这些需要判断的数,而 wait 数组就是用来存储这些数的。这样,我们就可以愉快地使用 的算法通过了。

#include <iostream>
#include <vector>
 
using namespace std;
 
const int kMaxN = 1e7 + 5;
 
int prv[kMaxN], nxt[kMaxN], b[kMaxN], t, id, n;  // prv 是上一个元素,nxt 是下一个,b 是标记数组
long long k;                                     // 最多删除次数,注意使用 long long
vector<int> v, f, wait;                          // 分别为上一次删的,下一步要删的和 wait 数组
char s[kMaxN];                                   // 用来存储字符串
 
// 判断第 x 个字符是否满足条件
bool check(int x) { return s[nxt[x]] == s[prv[x]]; }
 
int main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);  // 读入优化
  for (cin >> t >> id; t; t--) {                     // 多组数据
    cin >> n >> k >> (s + 1);                        // 输入信息
    f.clear();                                       // 清空第一次要删的
    // 初始化链表
    for (int i = 1; i <= n; i++) {
      prv[i] = i - 1, nxt[i] = i + 1;
    }
    // 第一次删除(没有真正删掉)
    for (int i = 2; i <= n; i++) {
      check(i) && (f.push_back(i), 0);
    }
    // 最多 k 次并且有要删的
    for (int i = 1; i <= k && f.size(); i++) {
      wait.clear();      // 清空 wait 数组
      for (int j : f) {  // 遍历要删的
        b[j] = 1;        // 真正删除
      }
      // 执行下一步删除
      for (int j = 0, k; j < f.size(); ) {
        for (k = j + 1; k < f.size() && f[k] - f[k - 1] == 1; k++) { }  // 找到一段连续的 [j, k)
        nxt[prv[f[j]]] = nxt[f[k - 1]];                                 // 对链表进行删除操作
        prv[nxt[f[k - 1]]] = prv[f[j]];
        wait.push_back(prv[f[j]]);                                      // 加入 wait 数组
        wait.push_back(nxt[f[k - 1]]);
        j = k;                                                          // 跳过去
      }
      // 对 wait 进行逐一排查
      for (int j : wait) {
        if (!b[j] && 1 < j && j < n && check(j)) {  // 如果满足条件
          v.push_back(j);                           // 加入下一步要删的
        }
      }
      f = v, v.clear();                             // 交换并清空
    }
    // 输出并清空
    for (int i = 1; i <= n; b[i++] = 0) {
      if (!b[i]) {
        cout << s[i];
      }
    }
    cout << '\n';
  }
  return 0;
}