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);
    }
}
posted @ 2020-03-17 20:57  Shiina_Mashiro  阅读(209)  评论(0编辑  收藏  举报