CF1149BThree Religions

CF1149BThree Religions

dp


\(a_{i,\ j}\) 为宗教 \(i\) 的第 \(j\) 位, \(nxt_{i,\ j}\) 为字符 \(i\)\(j\) 之后出现的第一个位置,不存在则为 \(n+1\)

\(f_{i,\ j,\ k}\) 为字符串中满足 \(1\) 宗教的前 \(i\) 位, \(2\) 宗教的前 \(j\) 位, \(3\) 宗教的前 \(k\) 位的前缀最小位置

则转移方程为 \(f_{i,\ j,\ k}=\min\{nxt_{a_{1,\ i},\ f_{i-1.\ j,\ k}},\ nxt{a_{2,\ j},\ f_{i,\ j-1,\ k}},\ nxt_{a_{3,\ k},\ f_{i,\ j,\ k-1}}\}\)

但这样是 \(O(26n+250^3q)\) 的,跑不过……

注意到每次修改后,有很多状态是重复的,因此只需修改受影响的状态即可

如果修改的是宗教 \(1\) ,显然 \(f_{i,\ j,\ k} | (i\in[1,\ len_1)\) 是不用修改的,只需重新计算 \(i=len_1\) 的所有状态,这样每次计算的状态就被减少到了 \(O(250^2)\)

时间复杂度 \(O(26n+250^2q)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 10;
char str[maxn], A[3][255];
int n, m, sz[3], nxt[26][maxn], dp[255][255][255];

inline int chkmin(int& x, int y) {
  return x < y ? x : x = y;
}

int main() {
  scanf("%d %d %s", &n, &m, str + 1);
  for (int j = 0; j < 26; j++) {
    for (int i = n, pos = n + 1; ~i; i--) {
      nxt[j][i] = pos;
      if (str[i] == j + 'a') pos = i;
    }
  }
  dp[0][0][0] = 0;
  char op[3]; int x;
  while (m--) {
    scanf("%s %d", op, &x), x--;
    if (*op == '+') {
      scanf("%s", op);
      A[x][++sz[x]] = *op;
      int s0 = x == 0 ? sz[0] : 0;
      int s1 = x == 1 ? sz[1] : 0;
      int s2 = x == 2 ? sz[2] : 0;
      for (int i = s0; i <= sz[0]; i++) {
        for (int j = s1; j <= sz[1]; j++) {
          for (int k = s2; k <= sz[2]; k++) {
            dp[i][j][k] = n + 1;
            if (i && dp[i - 1][j][k] <= n) chkmin(dp[i][j][k], nxt[A[0][i] - 'a'][dp[i - 1][j][k]]);
            if (j && dp[i][j - 1][k] <= n) chkmin(dp[i][j][k], nxt[A[1][j] - 'a'][dp[i][j - 1][k]]);
            if (k && dp[i][j][k - 1] <= n) chkmin(dp[i][j][k], nxt[A[2][k] - 'a'][dp[i][j][k - 1]]);
          }
        }
      }
    } else {
      sz[x]--;
    }
    puts(dp[sz[0]][sz[1]][sz[2]] <= n ? "YES" : "NO");
  }
  return 0;
}
posted @ 2019-05-01 19:52  cnJuanzhang  阅读(217)  评论(0编辑  收藏  举报