Non-Decreasing Subsequences
Bessie 最近参加了一场 USACO 竞赛,遇到了以下问题。当然 Bessie 知道怎么做。那你呢?
考虑一个仅由范围在 (1ldots K) 之间的整数组成的长为 (N) 的序列 (A_1,A_2,ldots ,A_N)。给定 (Q) 个形式为 ([L_i,R_i]) 的询问。对于每个询问,计算 (A_{L_i},A_{L_{i+1}},ldots ,A_{R_i}) 中不下降子序列的数量模 (10^9+7) 的余数。
(A_L,ldots ,A_R) 的一个不下降子序列是一组索引 ((j_1,j_2,ldots ,j_x)),满足 (Lle j_1<j_2<ldots <j_xle R) 以及 (A_{j_1}le A_{j_2}le ldots le A_{j_x})。确保你考虑了空子序列!
对于全部数据,(1le Kle 20,1le Nle 5 imes 10^4,1le Qle 2 imes 10^5,1le L_ile R_ile N)。
题解
http://jklover.hs-blog.cf/2020/06/06/Loj-3247-Non-Decreasing-Subsequences/
https://www.luogu.com.cn/blog/Karry5307/sol-p6009
矩阵乘法.
dp 显然可以写成转移矩阵的形式.
记 (v) 为长度为 (K) ,值全为 (1) 的列向量, (w) 为长度为 (K) ,仅第一个值为 (1) 的行向量, (T_i) 表示第 (i) 个数对应的转移矩阵.
那么询问 ([l,r]) 的答案就是
第 (r) 个位置的转移矩阵大概长这样:
[T_{r,i,j}=[i=j]+[ileq j][j=a_r] ]
注意到转移矩阵 (T_i) 有逆,且逆是容易直接求出的,于是可以考虑维护转移矩阵前缀积,以及逆矩阵的前缀积.
考虑这个东西的逆矩阵是啥。根据人类智慧得到
[T_{r,i,j}^{-1}=[i=j]-frac{1}{2}[ileq j][j=a_r] ]
记 (x_i=T_1T_2T_3cdots T_iv,y_i=wT_{i}^{-1}cdots T_{3}^{-1}T_2^{-1}T_1^{-1}) ,则询问的答案为 ((y_{l-1}x_r)_{0,0}) .
(T_i) 和 (T_{i}^{-1}) 都只有 (O(k)) 个位置有值,于是右乘 (T_{i}) 与左乘 (T_{i}^{-1}) 都可以 (O(K^2)) 完成.
时间复杂度 (O(nK^2+qK)) .
CO int N=5e4+10;
int K,a[N],op;
struct matrix {int v[20][20];} x[N],y[N];
matrix operator*(CO matrix&a,CO matrix&b){
matrix ans={};
if(!op){
for(int k=0;k<K;++k)
for(int j=0;j<K;++j)if(b.v[k][j])
for(int i=0;i<K;++i)if(a.v[i][k])
ans.v[i][j]=add(ans.v[i][j],mul(a.v[i][k],b.v[k][j]));
}
else{
for(int k=0;k<K;++k)
for(int i=0;i<K;++i)if(a.v[i][k])
for(int j=0;j<K;++j)if(b.v[k][j])
ans.v[i][j]=add(ans.v[i][j],mul(a.v[i][k],b.v[k][j]));
}
return ans;
}
int main(){
int n=read<int>();read(K);
for(int i=1;i<=n;++i) a[i]=read<int>()-1;
op=0;
for(int i=0;i<K;++i) x[0].v[i][i]=1;
for(int i=1;i<=n;++i){
matrix tmp={};
for(int j=0;j<K;++j) tmp.v[j][j]=1;
for(int j=0;j<=a[i];++j) ++tmp.v[j][a[i]];
x[i]=x[i-1]*tmp;
}
matrix tmp={};
for(int i=0;i<K;++i) tmp.v[i][0]=1;
for(int i=1;i<=n;++i) x[i]=x[i]*tmp;
op=1;
for(int i=0;i<K;++i) y[0].v[i][i]=1;
for(int i=1;i<=n;++i){
matrix tmp={};
for(int j=0;j<K;++j) tmp.v[j][j]=1;
for(int j=0;j<=a[i];++j) tmp.v[j][a[i]]+=mod-i2;
y[i]=tmp*y[i-1];
}
memset(tmp.v,0,sizeof tmp);
tmp.v[0][0]=1;
for(int i=1;i<=n;++i) y[i]=tmp*y[i];
for(int q=read<int>();q--;){
int l=read<int>(),r=read<int>(),ans=0;
for(int k=0;k<K;++k)
ans=add(ans,mul(y[l-1].v[0][k],x[r].v[k][0]));
printf("%d
",ans);
}
return 0;
}