LOJ#6074. 「2017 山东一轮集训 Day6」子序列

LOJ#6074. 「2017 山东一轮集训 Day6」子序列

先考虑全局询问怎么做,设 f ( i , c ) f(i,c) f(i,c) 表示在 S 1 ⋯ i S_{1\cdots i} S1i 中,最后一个选取 max ⁡ { j ∣ j ≤ i , S j = c } \max\{j|j\le i,S_j=c\} max{jji,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(i1,c),(c=Si)f(i,c)=j=09f(i1,j),(c=Si)
其中 f ( 0 , 0 ) = 1 f(0,0)=1 f(0,0)=1 特殊定义,字符集占位 1 ⋯ 9 1\cdots 9 19

考虑怎么运用到区间查询上。初始状态为

P 0 = [ 1 0 ⋯ 0 ] T P_0=\begin{bmatrix} 1 & 0 & \cdots & 0 \end{bmatrix}^T P0=[100]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=111111111
其中第 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=FnF2F1P0

试着对 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=Fi1=111111111

对角线为 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=FrFr1FlP0=F^rB^l1P0

或者写好一点,

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=[111]F^rB^l1[100]T
其中 F ^ n = F n F n − 1 ⋯ F 1 \hat F_n=F_nF_{n-1}\cdots F_1 F^n=FnFn1F1,即左乘前缀积; B ^ n = B 1 B 2 ⋯ B n \hat B_n=B_1B_2\cdots B_n B^n=B1B2Bn,即右乘前缀积。

如果直接做,复杂度是 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 [111]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[100]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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

日居月诸Rijuyuezhu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值