【线段树维护DFA】cf Deltix Round autumn E
遇到一道用线段树维护有限状态自动机的题,感觉还是很巧妙的(虽然我是小蜗蜗说这套题出得不行)
看的这位大佬的讲解
原题
题目大意:每次都会修改一个abc字符串中的某个字符求每次你要修改abc中的最少个数求得不包含abc的子序列。
考虑一个串不更改怎么做,我们可以看做是一个有限状态自动机,从左往右跑转移,然后根据当前字符是多少确定转移状态。eg:当前是0状态,a字符,那么从0->0代价1,0->1代价0,2->3代价1,2->2代价0,1->3代价inf(不可能转移)。
然后我们考虑线段树合并那么设f[i][j]表示从i转移到j所需的最小代价,那么合并时类似floyd,f[i][j]=min(fl[i][k]+fr[k][j])就可以了。
#include
using namespace std;
const int maxn = 4e5 + 5;
const int inf = 0x3f3f3f3f;
struct mtx {
int a[4][4]; // cost from a to b
mtx operator+(mtx bb) {
mtx o;
for (int i = 0; i <= 3; i++) {
for (int j = i; j <= 3; j++)
o.a[i][j] = inf;
}
for (int k = 0; k <= 3; k++) {
for (int i = 0; i <= k; i++) {
for (int j = k; j <= 3; j++) {
o.a[i][j] = min(o.a[i][j], a[i][k] + bb.a[k][j]);
}
}
}
return o;
}
} z[maxn];
int n, q, PS[maxn];
char ss[maxn];
void init(int p, int pos) {
switch (ss[pos]) {
case 'a':
z[p].a[0][0] = 1;
z[p].a[0][1] = 0;
z[p].a[0][2] = z[p].a[0][3] = z[p].a[1][3] = inf;
z[p].a[1][1] = z[p].a[2][2] = z[p].a[3][3] = 0;
z[p].a[1][2] = z[p].a[2][3] = 1;
break;
case 'b':
z[p].a[1][1] = 1;
z[p].a[1][2] = 0;
z[p].a[0][2] = z[p].a[0][3] = z[p].a[1][3] = inf;
z[p].a[0][0] = z[p].a[2][2] = z[p].a[3][3] = 0;
z[p].a[0][1] = z[p].a[2][3] = 1;
break;
case 'c':
z[p].a[2][2] = 1;
z[p].a[2][3] = 0;
z[p].a[0][2] = z[p].a[0][3] = z[p].a[1][3] = inf;
z[p].a[0][0] = z[p].a[1][1] = z[p].a[3][3] = 0;
z[p].a[0][1] = z[p].a[1][2] = 1;
break;
}
}
void maketree(int p, int l, int r) {
if (l == r) {
init(p, l);
PS[l] = p;
return;
}
int mid = (l + r) >> 1;
maketree(p << 1, l, mid);
maketree(p << 1 | 1, mid + 1, r);
z[p] = z[p << 1] + z[p << 1 | 1];
}
int main() {
scanf("%d%d", &n, &q);
scanf("%s", &ss[1]);
maketree(1, 1, n);
int x;
char ch;
for (int i = 1; i <= q; i++) {
scanf("%d %c", &x, &ch);
ss[x] = ch;
init(PS[x], x);
int p = (PS[x] >> 1);
while (p) {
z[p] = z[p << 1] + z[p << 1 | 1];
p >>= 1;
}
printf("%d\n", min(z[1].a[0][0], min(z[1].a[0][1], z[1].a[0][2])));
}
return 0;
}