LOJ#6074. 「2017 山东一轮集训 Day6」子序列
先考虑全局询问怎么做,设 f ( i , c ) f(i,c) f(i,c) 表示在 S 1 ⋯ i S_{1\cdots i} S1⋯i 中,最后一个选取 max { j ∣ j ≤ i , S j = c } \max\{j|j\le i,S_j=c\} max{j∣j≤i,Sj=c} 的子序列个数。本质上是在子序列自动机的 DAG 上计数路径,可以保证本质不同。
转移很简单:
f
(
i
,
c
)
=
f
(
i
−
1
,
c
)
,
(
c
≠
S
i
)
f
(
i
,
c
)
=
∑
j
=
0
9
f
(
i
−
1
,
j
)
,
(
c
=
S
i
)
\begin{gathered} f(i,c)=f(i-1,c),(c\ne S_i) \\ f(i,c)=\sum_{j=0}^9f(i-1,j),(c=S_i) \end{gathered}
f(i,c)=f(i−1,c),(c=Si)f(i,c)=j=0∑9f(i−1,j),(c=Si)
其中
f
(
0
,
0
)
=
1
f(0,0)=1
f(0,0)=1 特殊定义,字符集占位
1
⋯
9
1\cdots 9
1⋯9。
考虑怎么运用到区间查询上。初始状态为
P 0 = [ 1 0 ⋯ 0 ] T P_0=\begin{bmatrix} 1 & 0 & \cdots & 0 \end{bmatrix}^T P0=[10⋯0]T
转移为
F
i
=
[
1
1
⋱
1
1
1
1
1
1
⋱
1
]
F_i=\begin{bmatrix} 1 \\ &1\\ &&\ddots\\ 1 & 1 & 1& 1 & 1& 1\\ &&&&\ddots\\ &&&&&1 \end{bmatrix}
Fi=⎣⎢⎢⎢⎢⎢⎢⎡1111⋱111⋱11⎦⎥⎥⎥⎥⎥⎥⎤
其中第
S
i
S_i
Si 行为 1。
则有 P n = F n ⋯ F 2 F 1 P 0 P_n=F_n\cdots F_2F_1P_0 Pn=Fn⋯F2F1P0
试着对 F i F_i Fi 求个逆(可以用初等变换),发现
B i = F i − 1 = [ 1 1 ⋱ − 1 − 1 − 1 1 − 1 − 1 ⋱ 1 ] B_i=F_i^{-1}=\begin{bmatrix} 1 \\ &1\\ &&\ddots\\ -1 & -1 & -1& 1 & -1& -1\\ &&&&\ddots\\ &&&&&1 \end{bmatrix} Bi=Fi−1=⎣⎢⎢⎢⎢⎢⎢⎡1−11−1⋱−11−1⋱−11⎦⎥⎥⎥⎥⎥⎥⎤
对角线为 1,第 S i S_i Si 行除了第 S i S_i Si 列以外为 -1。
则我们求
P = F r F r − 1 ⋯ F l P 0 = F ^ r B ^ l − 1 P 0 P=F_rF_{r-1}\cdots F_lP_0=\hat F_r\hat B_{l-1}P_0 P=FrFr−1⋯FlP0=F^rB^l−1P0
或者写好一点,
a
n
s
=
[
1
1
⋯
1
]
F
^
r
B
^
l
−
1
[
1
0
⋯
0
]
T
ans=\begin{bmatrix} 1 & 1 & \cdots & 1 \end{bmatrix}\hat F_r\hat B_{l-1}\begin{bmatrix} 1 & 0 & \cdots & 0 \end{bmatrix}^T
ans=[11⋯1]F^rB^l−1[10⋯0]T
其中
F
^
n
=
F
n
F
n
−
1
⋯
F
1
\hat F_n=F_nF_{n-1}\cdots F_1
F^n=FnFn−1⋯F1,即左乘前缀积;
B
^
n
=
B
1
B
2
⋯
B
n
\hat B_n=B_1B_2\cdots B_n
B^n=B1B2⋯Bn,即右乘前缀积。
如果直接做,复杂度是 O ( ( n + q ) m 3 ) O((n+q)m^3) O((n+q)m3)。由于 F i , B i F_i,B_i Fi,Bi 都相当稀疏,考虑直接递推得出。
左乘 F n F_n Fn,相当于在原来基础上把第 S n S_n Sn 行的每个数变成了它们这一列的和。
考虑求 [ 1 1 ⋯ 1 ] F ^ n \begin{bmatrix}1 & 1 & \cdots & 1\end{bmatrix}\hat F_n [11⋯1]F^n,这相当于求它各列的和。于是我们维护每一列的和,即可 O ( m ) O(m) O(m) 单次转移。
右乘 B n B_n Bn,相当于在原来基础上将每一行的每一个数(除了第 S i S_i Si 列的数)都减去当前行第 S i S_i Si 列的数。
考虑求 B ^ n [ 1 0 ⋯ 0 ] T \hat B_n\begin{bmatrix} 1 &0 & \cdots & 0\end{bmatrix}^T B^n[10⋯0]T,这相当于求它的第一列的每个数。维护第一列每个数的值和每行的全局增减标记即可。
时间复杂度 O ( ( n + m ) q ) O((n+m)q) O((n+m)q)。
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long ll;
ll read() {
ll x = 0, f = 1; char ch = getchar();
for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + int(ch - '0');
return x * f;
}
const int MAXN = 1e5 + 5, P = 1e9 + 7, M = 9;
namespace MINT {
struct mint {
int v;
mint(int v = 0) : v(v) {}
};
int MOD(int v) {return v >= P ? v - P : v;}
mint tran(int v) {return MOD(v % P + P);}
mint operator + (mint a, mint b) {return MOD(a.v + b.v);}
mint operator - (mint a, mint b) {return MOD(a.v - b.v + P);}
mint operator * (mint a, mint b) {return 1ll * a.v * b.v % P;}
mint qpow(mint a, int n=P-2) {mint ret = 1; for(; n; n >>= 1, a = a * a) if(n & 1) ret = ret * a; return ret;}
} using namespace MINT;
char s[MAXN];
int n, q;
mint f[MAXN][M+1], b[MAXN][M+1], tag[MAXN][M+1], matf[M+1][M+1], matb[M+1][M+1];
int main() {
scanf("%s", s+1); n = strlen(s+1);
q = read();
for(int i = 0; i <= M; i++) f[0][i] = 1, matf[i][i] = 1, matb[i][i] = 1, tag[0][i] = 0;
b[0][0] = 1;
for(int i = 1; i <= n; i++) {
int c = s[i] - 'a' + 1;
for(int j = 0; j <= M; j++) {
f[i][j] = f[i-1][j] - matf[c][j] + f[i-1][j];
matf[c][j] = f[i-1][j];
tag[i][j] = tag[i-1][j] - (matb[j][c] + tag[i-1][j]); matb[j][c] = matb[j][c] + (matb[j][c] + tag[i-1][j]);
b[i][j] = matb[j][0] + tag[i][j];
}
}
for(int i = 1; i <= q; i++) {
int l = read(), r = read();
mint ans = 0;
for(int j = 0; j <= M; j++) ans = ans + f[r][j] * b[l-1][j];
printf("%d\n", (ans-1).v);
}
return 0;
}