1 条题解
-
0
自动搬运
来自洛谷,原作者为

rainygame
There is no trap so deadly as the trap you set for yourself.搬运于
2025-08-24 23:11:25,当前版本为作者最后更新于2025-03-22 00:19:35,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
出现至少两次的本质不同子序列不好计数,容斥一下就可以变为两个问题:求本质不同子序列个数、求出现恰好一次的本质不同子序列个数。
求本质不同子序列个数
设 表示前 个数构成的以 为结尾的本质不同子序列个数。那么有转移:
$$f_{i,j}=\begin{cases} f_{i-1,j}, &s_i\ne j\\ \sum\limits_kf_{i-1,k}+1, &s_i=j \end{cases} $$这表示对于一个子序列,每次选择的点都是尽量靠后的。这保证了只计算一次。
显然这个形式可以写成矩阵乘法的形式,然后直接上线段树维护矩阵乘法即可。此处时间复杂度 ,其中 。
求出现恰好一次的本质不同子序列个数
“出现恰好一次”等价于“既是第一个也是最后一个”。举个
abc的例子。这说明这个子序列中的a前面不能有a,这个子序列中的a、b之间不能有a或b、这个子序列中的b、c之间不能有b或c、这个子序列中的c后面不能有c。如果满足了这些条件,那么可以推得abc这个子序列在串中唯一出现。这个不是很好 DP,因为还好考虑到两个字符之间的字符集。但是因为是在线段树上做,所以可以改成区间信息合并。区间需要维护 表示区间内以 开头 结尾的唯一出现子序列个数, 分别表示序列中第一个/最后一个 的前面/后面的字符集。合并过程为:
$$f(x,y)=\sum\limits_{a}\sum\limits_{b}[a,b \notin (s(a)\cup p(b))]f_l(x,a)f_r(b,y) $$注意还需要特判某个唯一出现子序列在某一边的情况。
这样就可以做到 了,由于 15s 的超长时限,应该可以通过。但是还可以优化。
容易发现这是一个类似矩阵乘法的东西,设 $A_{i,j}=f_l(i,j),B_{i,j}=[i,j \notin (s(i)\cup p(j))],C_{i,j}=f_r(i,j)$,那么显然 。时间复杂度 。
代码很好写:
#include <bits/stdc++.h> using namespace std; #define int long long #define MAXN 50005 const int MOD(998244353); int n, q; string t; struct Matrix{ int n, m, a[7][7]; Matrix(){memset(a, 0, sizeof(a));} Matrix operator*(Matrix b){ Matrix c; c.n = n; c.m = b.m; for (int i(0); i<n; ++i) for (int j(0); j<b.m; ++j) for (int k(0); k<m; ++k){ (c.a[i][j] += a[i][k]*b.a[k][j]) %= MOD; } return c; } void print(){for (int i(0); i<n; ++i, cerr << '\n') for (int j(0); j<m; ++j) cerr << a[i][j] << ' ';} }; namespace Seg{ struct Node{Matrix f, g; int st, p[6], s[6];}tr[MAXN<<2]; void modify(int p, int l, int r, int x, int k){ if (l == r){ tr[p].st = 1<<k; tr[p].p[k] = tr[p].s[k] = 0; tr[p].f.n = tr[p].f.m = 7; tr[p].g.n = tr[p].g.m = 6; for (int i(0); i<6; ++i) if (i ^ k) tr[p].p[i] = tr[p].s[i] = 63; tr[p].f.a[6][6] = 1; for (int i(0); i<6; ++i) for (int j(0); j<6; ++j) tr[p].f.a[i][j] = i == j || j == k; for (int i(0); i<6; ++i) tr[p].f.a[6][i] = i == k; for (int i(0); i<6; ++i) for (int j(0); j<6; ++j) tr[p].g.a[i][j] = i == j && j == k; return; } int mid((l+r)>>1); if (x <= mid) modify(p<<1, l, mid, x, k); else modify(p<<1|1, mid+1, r, x, k); tr[p].f = tr[p<<1].f * tr[p<<1|1].f; tr[p].st = tr[p<<1].st | tr[p<<1|1].st; for (int i(0); i<6; ++i) tr[p].p[i] = min(tr[p<<1].p[i], tr[p<<1|1].p[i]|tr[p<<1].st); for (int i(0); i<6; ++i) tr[p].s[i] = min(tr[p<<1|1].s[i], tr[p<<1].s[i]|tr[p<<1|1].st); Matrix yyx; for (int i(0); i<6; ++i) for (int j(0); j<6; ++j) yyx.a[i][j] = !((1<<i|1<<j)&(tr[p<<1].s[i]|tr[p<<1|1].p[j])); yyx.n = yyx.m = 6; tr[p].g = tr[p<<1].g * yyx * tr[p<<1|1].g; for (int i(0); i<6; ++i) for (int j(0); j<6; ++j){ if (!(tr[p<<1].st>>i&1)) tr[p].g.a[i][j] += tr[p<<1|1].g.a[i][j]; if (!(tr[p<<1|1].st>>j&1)) tr[p].g.a[i][j] += tr[p<<1].g.a[i][j]; } } } int sol(){ Matrix f(Seg::tr[1].f), g(Seg::tr[1].g); Matrix a; a.a[0][6] = a.n = 1; a.m = 7; a = a*f; int res(0); for (int i(0); i<6; ++i) (res += a.a[0][i]) %= MOD; for (int i(0); i<6; ++i) for (int j(0); j<6; ++j) (res += MOD-g.a[i][j]) %= MOD; return res; } signed main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> q >> t; t = ' ' + t; for (int i(1); i<=n; ++i) Seg::modify(1, 1, n, i, t[i]-'a'); cout << sol() << '\n'; for (int x; q; --q){ char c; cin >> x >> c; Seg::modify(1, 1, n, x, c-'a'); cout << sol() << '\n'; } return 0; }
- 1
信息
- ID
- 11717
- 时间
- 15000ms
- 内存
- 1024MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者