1 条题解
-
0
自动搬运
来自洛谷,原作者为

cwfxlh
会赢的!搬运于
2025-08-24 22:16:29,当前版本为作者最后更新于2023-07-14 20:25:55,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
P6009
模拟赛考了这道题,但不知道为什么没做出来。
DP 式子的转移是很好列的,令 表示当前以值为 的元素结尾的不降子序列个数,特别的,令 表示空序列个数,则令当前枚举到了第 个元素,转移为 。很容易发现这个东西可以写成矩阵的形式,于是问题就变成了一个区间矩阵乘积的问题。我们不妨用前缀积与逆前缀积来维护。但是我们发现,转移写成的矩阵逆是很好求的,不需要高消求逆(数学计算推一下即可,形式与原矩阵相似),于是直接暴力乘出一个前缀积和逆前缀积,询问的时候相乘即可。复杂度 。
但是这个复杂度是过不去的,我们发现,转移写出来的矩阵只有 个元素有值,于是预处理的矩乘就可以加速到 。询问的时候,答案矩阵可以由 得到,其中 是一个仅有一个元素为 1 的 的矩阵,于是乘上 后,矩阵大小只有 ,暴力矩阵乘的复杂度会变为 ,可以通过此题。当然利用前缀和可以优化到 。总复杂度 。
代码:
#include<bits/stdc++.h> #define MOD 1000000007 #define ll long long using namespace std; struct Matrix{ int h; int w; int v[23][23]; }InvPre[50003],Pre[50003],res,ans; Matrix operator*(Matrix A,Matrix B){ res.h=A.h; res.w=B.w; for(int i=1;i<=res.h;i++){ for(int j=1;j<=res.w;j++){ res.v[i][j]=0; for(int u=1;u<=A.w;u++)res.v[i][j]=(res.v[i][j]+((ll)(A.v[i][u])*(ll)(B.v[u][j])%MOD))%MOD; } } return res; } void print(Matrix A){ for(int i=1;i<=A.h;i++){ for(int j=1;j<=A.w;j++)printf("%d ",(A.v[i][j]+MOD)%MOD); printf("\n"); } return; } int n,k,q,w[50003],op1,op2,sum; int main(){ scanf("%d%d",&n,&k); for(int i=1;i<=n;i++)scanf("%d",&w[i]); scanf("%d",&q); Pre[0].h=Pre[0].w=InvPre[0].h=InvPre[0].w=k+1; for(int i=1;i<=k+1;i++)Pre[0].v[i][i]=InvPre[0].v[i][i]=1; for(int i=1;i<=n;i++){ Pre[i].h=Pre[i].w=InvPre[i].h=InvPre[i].w=k+1; Pre[i]=Pre[i-1]; for(int u=1;u<=k+1;u++){ for(int p=1;p<=w[i]+1;p++)Pre[i].v[u][w[i]+1]=(Pre[i].v[u][w[i]+1]+Pre[i-1].v[u][p])%MOD; } for(int j=1;j<=k+1;j++){ for(int u=1;u<=k+1;u++){ InvPre[i].v[j][u]=(InvPre[i-1].v[j][u]-(500000004ll*(ll)(InvPre[i-1].v[w[i]+1][u])*(ll)(j<=w[i]+1))%MOD)%MOD; } } } while(q--){ scanf("%d%d",&op1,&op2); ans.h=1; ans.w=k+1; for(int i=1;i<=k+1;i++)ans.v[1][i]=(int)(i==1); ans=(ans*InvPre[op1-1]); ans=(ans*Pre[op2]); sum=0; for(int i=1;i<=k+1;i++)sum=(sum+ans.v[1][i])%MOD; sum+=MOD; sum%=MOD; printf("%d\n",sum); } return 0; }
- 1
信息
- ID
- 5018
- 时间
- 2000ms
- 内存
- 250MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者