题意:
茉优最近研究发现,一个人的想愿能力可以认为是字符串
S
S
S的一个子串
S
[
l
,
r
]
S[l,r]
S[l,r],而连接值可以认为是这个子串的本质不同子序列个数。现在她想验证她的结论是否正确,于是她给了你
Q
Q
Q个询问,希望你帮她来计算,注意空串也是子序列。
数据范围:
Analysis:
考虑一个暴力的
D
p
Dp
Dp,设
f
i
f_i
fi表示以下标
i
i
i结尾的本质不同子序列个数,每次转移就是前面所有
f
i
f_i
fi的和,考虑如果结尾相同会算重,于是我们把所有与
s
i
s_i
si相同的
s
j
s_j
sj的
f
j
f_j
fj减去。这样可以得到一个
O
(
Q
∣
S
∣
)
O(Q|S|)
O(Q∣S∣)的算法。
我们考虑多设一维状态把
f
i
f_i
fi前缀和,
f
i
,
j
f_{i,j}
fi,j表示到
i
i
i,以字符
j
j
j结尾的不同子序列个数。这种区间询问动态
D
p
Dp
Dp,一般都是转成矩阵的形式来做。
我们考虑它的转移矩阵大约是一个对角线上全为
1
1
1,且
S
i
S_i
Si对应的一列上全为
1
1
1的形式。
并且考虑多开一行一列表示空串的方案。
没有修改,我们可以预处理从左到右乘的前缀积矩阵,从右到左乘的前缀积逆矩阵。
暴力转移是
5
2
3
52^3
523的,过不去。挖掘矩阵性质。
对于正矩阵:发现每一次转移,除了中间全是
1
1
1的一列,其他地方不变,而中间那一列则是变成每一行的和。那么我们可以动态维护每一行的和,这样一次转移复杂度就是
52
52
52了。
对于逆矩阵:同理观察,发现是每一次每一列减去某一行的一个值,我们考虑打减法标记,这样也可以动态维护过去。
我们就完成了
O
(
52
∗
∣
S
∣
)
O(52*|S|)
O(52∗∣S∣)的预处理。
考虑怎么求答案?
我们需要的是:
[
0
,
0
,
0
,
0
,
.
.
.
,
1
]
∗
B
l
−
1
−
1
∗
B
r
[0,0,0,0,...,1]*B^{-1}_{l-1}*B_r
[0,0,0,0,...,1]∗Bl−1−1∗Br。
B
B
B是预处理的前缀积矩阵。
那么乘上逆矩阵会变成逆矩阵的最后一行,然后再乘
B
r
B_r
Br。
我们最后只需要这个行向量的每一项的和,发现乘
B
r
B_r
Br的话,每一项都乘了对应行的和。
因此我们只要维护出每一个逆矩阵的最后一行,以及正矩阵每一行的和就行了。
复杂度
O
(
(
∣
S
∣
+
Q
)
∗
52
)
O((|S|+Q)*52)
O((∣S∣+Q)∗52)
Code:
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 1e6 + 5;
const int M = 52;
const int mo = 998244353;
typedef long long ll;
char s[N];
int las[N][M + 5],S[N][M + 5];
int tag[M + 5],H[M + 5],now[M + 5][M + 5];
int n,Q,a,b,p,q,r,ans;
inline int ts(char c) { return c >= 'a' ? c - 'a' + 26 : c - 'A'; }
int main()
{
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
scanf("%s%d%d%d%d%d%d",s + 1,&Q,&a,&b,&p,&q,&r); n = strlen(s + 1);
for (int i = 0 ; i <= M ; ++i) H[i] = now[i][i] = 1;
for (int i = 1 ; i <= n ; ++i)
{
int L = ts(s[i]);
for (int j = 0 ; j <= M ; ++j)
{
int c = now[j][L];
now[j][L] = H[j],H[j] = ((ll)H[j] + now[j][L] - c + mo) % mo;
}
for (int j = 0 ; j <= M ; ++j) S[i][j] = H[j];
} memset(now,0,sizeof(now));
for (int i = 0 ; i <= M ; ++i) now[i][i] = 1;
for (int i = 1 ; i <= n ; ++i)
{
int L = ts(s[i]);
for (int j = 0 ; j <= M ; ++j)
{
now[L][j] = (now[L][j] + tag[j]) % mo;
tag[j] = (tag[j] + mo - now[L][j]) % mo,now[L][j] = (now[L][j] + mo - tag[j]) % mo;
}
for (int j = 0 ; j <= M ; ++j) las[i][j] = (now[M][j] + tag[j]) % mo;
}
while (Q--)
{
int a0 = a,b0 = b;
a = ((ll)p * a0 % mo + (ll)q * b0 % mo + ans + r) % mo;
b = ((ll)p * b0 % mo + (ll)q * a0 % mo + ans + r) % mo;
int l = min(a % n,b % n) + 1,r = max(a % n,b % n) + 1;
if (l == 1) ans = S[r][M];
else
{
ans = 0;
for (int j = 0 ; j <= M ; ++j) ans = (ans + (ll)las[l - 1][j] * S[r][j] % mo) % mo;
}
} printf("%d\n",ans);
return 0;
}