Luogu P6009 [USACO20JAN]Non-Decreasing Subsequences P
Link
先考虑计算\(l=1,r=n\)时的答案。
很显然我们可以dp,设\(f_{i,j}\)表示考虑前\(i\)个数,NDS末尾为\(j\)的方案数,那么转移为:
\[f_{i,j}=
\begin{cases}
f_{i-1,j}&j\ne a_i\\
f_{i-1,j}+\sum\limits_{l=1}^{a_i}f_{i-1,l}&j=a_i
\end{cases}
\]
考虑写成矩阵的形式,设\(F_i=\begin{pmatrix}f_{i,1}&\cdots&f_{i,k}\end{pmatrix},T_x\),其中\(T_{x,i,j}=[i=j]+[i\le x\wedge j=x]\)。
那么答案为:
\[\begin{pmatrix}1&0&\cdots&0\end{pmatrix}\prod\limits_{i=1}^nT_{a_i}\begin{pmatrix}1\\\vdots\\1\end{pmatrix}
\]
然后我们考虑一般的情况,不难发现\(T_{x,i,j}^{-1}=[i=j]-\frac12[i\le x\wedge j=x]\)。
考虑预处理:
\[\begin{aligned}
p_i&=\prod\limits_{j=1}^iT_{a_j}\begin{pmatrix}1\\\vdots\\1\end{pmatrix}\\
q_i&=\begin{pmatrix}1&0&\cdots&0\end{pmatrix}\prod\limits_{j=i}^1T_{a_i}^{-1}
\end{aligned}
\]
那么询问\((l,r)\)的答案就是\(q_{l-1}p_r\)。
注意右乘\(T_x\)和左乘\(T_x^{-1}\)均可以\(O(n^2)\)完成,因此时间复杂度为\(O(nk^2+qk)\)。
#include <cstdio>
const int N=50007,K=27,P=1000000007;
int a[N],mat[K][K],f[N][K],g[N][K];
int read(){int x;scanf("%d",&x);return x;}
void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
int div(int x){return x&1? (x+P)/2:x/2;}
int main()
{
int n=read(),k=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=k;++i) for(int j=1;j<=k;++j) mat[i][j]=i==j;
for(int t=1;t<=n;++t)
{
for (int i=1;i<=a[t];++i) for(int j=a[t];j>=i;--j) inc(mat[i][a[t]],mat[i][j]);
for(int i=1;i<=k;++i) for(int j=i;j<=k;++j) inc(f[t][i],mat[i][j]);
}
for(int i=1;i<=k;++i) for(int j=i;j<=k;++j) mat[i][j]=i==j;
for(int t=1;t<=n;++t)
{
for(int i=1;i<=k;++i) g[t][i]=mat[1][i];
for(int i=1;i<=a[t];++i) for(int j=a[t];j<=k;++j) inc(mat[i][j],P-div(mat[a[t]][j]));
}
for(int q=read(),l,r,ans;q;--q)
{
l=read(),r=read(),ans=0;
for(int i=1;i<=k;++i) inc(ans,1ll*g[l][i]*f[r][i]%P);
printf("%d\n",ans);
}
}