BZOJ3692 : 愚蠢的算法
两个函数相同等价于不存在长度为$3$的下降子序列。
先考虑随意填的部分,设$f[i][j]$表示考虑了$[i,n]$,下降子序列第$2$项的最小值的是这里面第$j$个的方案数,转移则考虑往序列里插数字,可以通过后缀和优化到$O(n^2)$。
然后考虑已固定的部分,设$g[i][j]$表示考虑了$[i,n]$,下降子序列第$2$项的最小值是$j$的方案数,因为填法唯一,所以直接转移即可。
时间复杂度$O(n^2)$。
#include<cstdio> const int N=2005,P=1000000007; int n,m,i,j,k,flag,a[N],v[N],b[N],cnt,f[N][N],s[N][N],g[N][N],ans; int main(){ scanf("%d%d",&n,&m); for(i=1;i<=m;i++)scanf("%d",&a[i]),v[a[i]]=1; f[n][0]=1; for(i=n-1;i>m;i--){ for(j=n-i+1;j>1;j--)f[i][j]=(s[i+1][j-1]+1)%P; for(j=n;j;j--)s[i][j]=(s[i][j+1]+f[i][j])%P; f[i][0]=1; } if(!m){ for(i=0;i<=n;i++)ans=(ans+f[1][i])%P; return printf("%d",ans),0; } for(i=1;i<=n;i++)if(!v[i])b[++cnt]=i; for(i=0;i<=n-m;i++)g[m+1][b[i]]=f[m+1][i]; if(m==n)g[n+1][0]=1; for(i=m;i;i--){ for(flag=1,j=i+1;j<=m;j++)if(a[i]>a[j]){flag=0;break;} for(j=1;j<=cnt;j++)if(a[i]>b[j]){flag=0;break;} for(j=0;j<=n;j++){ if(j&&a[i]>j)continue; if(flag)k=flag?j:a[i]; g[i][k]=(g[i][k]+g[i+1][j])%P; } } for(i=0;i<=n;i++)ans=(ans+g[1][i])%P; return printf("%d",ans),0; }