<cplusoj.com/d/senior/p/SS240827B>
这道题时间够慢慢做还是能做的,至少思路是很顺的,就是系数太难调了
考虑一个朴素的dp, f [ i ] [ c ] [ j ] [ t ] f[i][c][j][t] f[i][c][j][t] 表示考虑前 i i i 个字符,文章末尾有 j j j 个 c c c,当前剪贴板上是 t t t 的概率。这个dp的转移时显然的,这样子复杂度是 O ( n 2 q 2 6 2 ) O(n^2q26^2) O(n2q262)
i i i 显然可以滚掉, j j j 的处理是个经典套路,直接丢状态里就行,因为我们并不涉及期望的高次运算。
现在我们相当于在dp一个 g ( c , t ) g(c,t) g(c,t),我们直接dp复杂度是 O ( n q 2 6 2 ) O(nq26^2) O(nq262),像这种每次修改一个字符的题我们肯定是想ddp的,但是目前状态太大,我们考虑能不能搞掉一个26。
假设我们把 g g g 数组看成一个方阵,我们重新看一下代码,我们发现其实我们只需要维护 g g g 每一行的和、每一列的和、对角线上的每个值,我们就能够求到答案了。
写完之后在看你会理解它的组合意义:
- 每一行的和:文末为 c c c 的概率
- 每一列的和:剪贴板为 c c c 的概率
- 对角线上的值:文末为 c c c,且剪切板也为 c c c
而当我们把这份程序写好,然后整理一下,我们可以发现一个事实:每个字母的转移时独立
我们重新思考回题目,对于每个字母,只关心这个位置的字母是不是自己,所以肯定是独立的
因此对于每个字母我们都可以很轻松拿一个矩阵来维护,然后直接上线段树即可。复杂度是 O ( 5 3 ( n + q ) log n ) O(5^3(n+q)\log n) O(53(n+q)logn)
然而这样子会被卡常,然后你思考一下发现因为你开了26棵线段树,有些地方是完全没用被用掉的,你可以把线段树改成动态开点即可。
#include<bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stdout, ##__VA_ARGS__)
#define debag(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define debug(...) void(0)
#define debag(...) void(0)
#endif
//#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//#define M
#define mo 998244853
#define N 200010
int pw(int A, int b) {
long long ans = 1, a = A;
while(b) {
if(b & 1) ans *= a;
a *= a; b >>= 1;
ans %= mo; a %= mo;
}
return (int)ans;
}
int pw(int a) { return pw(a, mo - 2); }
int fac[N], inv[N], ifac[N];
void init(int n) {
int i;
for(i = fac[0] = 1; i <= n; ++i) fac[i] = fac[i-1] * i % mo;
ifac[n] = pw(fac[n], mo-2);
for(i = n - 1; i >= 0; --i) ifac[i] = ifac[i+1] * (i+1) % mo;
for(i = 1; i <= n; ++i) inv[i] = ifac[i] * fac[i-1] % mo;
}
int C(int n, int m) {
if(m > n) return 0;
return fac[n] * ifac[m] % mo * ifac[n - m] % mo;
}
inline void Mod(int &a) { if(a >= mo || a <= -mo) a %= mo; if(a < 0) a += mo; }
inline void Add(int &a, int b) { a += b; Mod(a); }
inline void Mul(int &a, int b) { Mod(b); a *= b; Mod(a); }
const int iv2 = pw(2, mo - 2);
const int iv3 = 1ll * iv2 * iv2 % mo;
int n, m, i, j, k, T;
int ans, c, q, t, S;
char s[N], str[5];
long long ys[5][5];
struct Martix {
int a[5][5], i;
void clear() { memset(a, 0, sizeof(a)); }
void init() { clear(); for(i = 0; i < 5; ++i) a[i][i] = 1; }
int qans() { return a[2][4]; }
}Common, Supper, Com[N];
Martix operator * (Martix A, Martix B) {
Martix C; int i, j, k;
for(i = 0; i < 5; ++i) for(j = 0; j < 5; ++j) ys[i][j] = 0;
for(i = 0; i < 5; ++i)
for(j = 0; j < 5; ++j)
for(k = 0; k < 5; ++k)
ys[i][k] += 1ll * A.a[i][j] * B.a[j][k];
for(i = 0; i < 5; ++i) for(j = 0; j < 5; ++j) C.a[i][j] = ys[i][j] % mo;
return C;
}
void init() {
Common.clear();
int a[5][5] = {
{iv2, 0 , 0 , 0 , 0},
{iv3, iv2, 0 , iv3, 0},
{iv3, 0 , 1 , iv3, 0},
{iv3, 0 , 0 , iv2, 0},
{0 , 0 , 0 , 0 , 1},
};
memcpy(Common.a, a, sizeof(a));
Supper.clear();
int b[5][5] = {
{iv2, 0 , 0 , 0 , iv2},
{iv3, iv2 + iv3, 0 , iv3, iv3},
{iv3, iv3 , 1 , iv3, iv3},
{iv3, iv2 , 0 , iv2, iv3},
{0 , 0 , 0 , 0 , 1 },
};
memcpy(Supper.a, b, sizeof(b));
}
#define M (1 << 23)
int ls[M], rs[M], tot;
Martix a[M];
struct Segment_tree {
//#define ls (k << 1)
//#define rs (k << 1 | 1)
#define mid ((l + r) >> 1)
void make_new(int &k, int l, int r) {
k = ++tot; a[k] = Com[r - l + 1];
}
void push_up(int k) { a[k] = a[rs[k]] * a[ls[k]]; }
void build(int &k, int l, int r){
make_new(k, 1, n);
make_new(ls[k], 1, mid);
make_new(rs[k], mid + 1, r);
}
void modify(int k, int l, int r, int x, int o) {
if(l == r) {
if(o == 0) a[k] = Common;
else a[k] = Supper;
return ;
}
if(!ls[k]) make_new(ls[k], l, mid), make_new(rs[k], mid + 1, r);
if(x <= mid) modify(ls[k], l, mid, x, o);
else modify(rs[k], mid + 1, r, x, o);
push_up(k);
}
#undef ls
#undef rs
#undef mid
}Seg[26];
int rt[26];
signed main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
// srand(time(NULL));
// T=read();
// while(T--) {
//
// }
init();
n = read(); q = read();
for(Com[0].init(), i = 1; i <= n; ++i) Com[i] = Common * Com[i - 1];
scanf("%s", s + 1); for(i = 1; i <= n; ++i) s[i] -= 'a';
for(i = 0; i < 26; ++i) Seg[i].build(rt[i], 1, n);
for(i = 1; i <= n; ++i) {
// s[i] -= 'a';
Seg[s[i]].modify(rt[s[i]], 1, n, i, 1);
}
while(q--) {
i = read(); scanf("%s", str); str[0] -= 'a';
Seg[s[i]].modify(rt[s[i]], 1, n, i, 0);
Seg[str[0]].modify(rt[str[0]], 1, n, i, 1);
ans = 0; s[i] = str[0];
for(i = 0; i < 26; ++i) {
Add(ans, a[rt[i]].qans());
}
printf("%d\n", ans);
}
return 0;
}