DMY2026下-Day7 T2 题解
大部分是赛时思路
首先我们看到条件发现这个东西就相当于每次往后面加一个除掉 border 的原串,然后我们接下来试着说明就是你可以添加的集合始终不变。
首先我们知道 border 跟字符串周期有一些很优美的性质,就是原串除掉 border 之后一定是一个周期,然后我们发现就是

由于 len 是一个周期,那么红色段和绿色段就一定相同,于是原串的 border 在经过一次操作之后依旧是 border。
然后我们考察这些串的性质:

其中红色划开是最短的周期,然后我们发现每条红线之前的子串都是一个 border,然后我们发现还没有考虑的 border 就只剩下了第一个小段的 border。
我们称红线划开之后的可以添加的串为普通串,其他 border 之后的串为特殊串,我们发现普通串一定成周期,看图就可以理解,然后我们试着说明特殊串一定无法通过普通串组成重复的,这个是显然的,然后特殊串之间也不会形成重复,于是可能形成重复的串就只剩下普通串了。
我们称普通串有 \(A\) 个,特殊串有 \(B\) 个,然后我们考虑一种一一对应答案和普通串的方式。
我们先考虑那些可以通过至多 \(t-1\) 次添加的答案,然后再减去那些无论如何都达不到 \(t-1\) 次的答案,
我们假如使用了非最长的那个普通串,那么之后如果不使用特殊串就会造成重复,因为你如果使用了连续的两个非最长的普通串,那么就可以合并,就会重复,于是我们钦定我们目前考虑的串在使用了非最长的那个普通串之后只能添加特殊串,那么至多 \(t-1\) 次添加的答案就可以通过 dp 来解决。
我们设 \(dp_{(i, 0/1)}\) 表示考虑到前 \(i\) 次操作,是否钦定了第 \(i+1\) 次操作必须使用特殊串的方案数,有转移:
然后答案就为 \(\sum_{i=0}^{t-1} dp_{(i, 0)} + dp_{(i, 1)}\)。
然后我们考虑减去那些无论如何都到达 \(t-1\) 次操作的串,那么为了让串尽可能符合条件,我们肯定把非最短的普通串拆成最短的普通串来数,因为普通串之间成周期,然后相当于现在就有 \(B\) 个特殊串和一个普通串,然后无法到达 \(t-1\) 次操作的数量就是 $\sum_{i=0}^{t-2} (B + 1)^i $。
如何优化呢?第一部分可以矩阵乘法,第二部分可以直接等比数列求和。
实现细节就是特判 \(B=0\) 的等比数列。
//奇跡を信じて,願いを叶えて
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define uint unsigned long long
#define Air
namespace io{
inline int read(){
int x; cin >> x; return x;
}
inline void write(int x){
if(x < 0){putchar('-'); x = -x;}
if(x >= 10){write(x / 10);}
putchar(x % 10 + '0');
}
}
using namespace io;
int n, q;
string s;
const int N = 1e6 + 10, MOD = 998244353;
int nxt[N];
int dp[N];
int quick_pow(int a, int b){
if(!b) return 1;
if(b & 1){
return a * quick_pow(a, b - 1) % MOD;
}
else{
int tmp = quick_pow(a, b / 2);
return tmp * tmp % MOD;
}
}
int dep[N];
struct Matr{
int n, m;
int a[4][4];
friend Matr operator * (Matr x, Matr y){
Matr ans;
int l1 = x.n, l2 = x.m, l3 = y.m;
ans.n = x.n; ans.m = y.m;
for(int i = 1; i <= l1; i++){
for(int j = 1; j <= l3; j++){
ans.a[i][j] = 0;
for(int k = 1; k <= l2; k++){
ans.a[i][j] += x.a[i][k] * y.a[k][j];
ans.a[i][j] %= MOD;
}
}
}
return ans;
}
Matr (int x = 0, int y = 0){
n = x;
m = y;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
a[i][j] = 0;
}
}
for(int i = 1; i <= n; i++){
a[i][i] = 1;
}
}
};
Matr quick_matr(Matr a, int b){
Matr res(a.n, a.m);
while(b){
if(b & 1) res = res * a;
a = a * a;
b >>= 1;
}
return res;
}
signed main() {
#ifdef Air
freopen(".in","r",stdin);
freopen(".out","w",stdout);
#endif
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
n = read();
q = read();
cin >> s;
s = ' ' + s;
dep[1] = 1;
for(int i = 2, j = 0; i <= n; i++){
while(j && s[j + 1] != s[i]){
j = nxt[j];
}
if(s[j + 1] == s[i]) j++;
nxt[i] = j;
dep[i] = dep[nxt[i]] + 1;
// cerr << nxt[i] << ' ';
}
while(q--){
int pos = read(), t = read();
int val = pos - nxt[pos];
int len = pos / val;
// cerr << val << '\n';
if(pos % val == 0){//整周期
t --;
cout << ((len * t - t + 1) % MOD + MOD) % MOD << '\n';
continue;
}
int tpos = pos - len * val;
// cerr << tpos << '\n';
int cnt = dep[tpos];
Matr per(3, 3);
per.a[1][1] = 1 + cnt; per.a[1][2] = len - 1; per.a[1][3] = len + cnt;
per.a[2][1] = cnt; per.a[2][2] = 0; per.a[2][3] = cnt;
per.a[3][1] = 0; per.a[3][2] = 0; per.a[3][3] = 1;
per = quick_matr(per, t - 1);
Matr ans(1, 3);
ans.a[1][1] = 1; ans.a[1][2] = 0; ans.a[1][3] = 1;
ans = ans * per;
int tot = ans.a[1][3];
int del = (quick_pow(cnt + 1, t - 1) - 1) * quick_pow(cnt, MOD - 2) % MOD;
tot += MOD - del;
tot %= MOD;
cout << tot << '\n';
}
return 0;
}

浙公网安备 33010602011771号