感谢大哥送来的 Fe
赛时送我打铁的一道题,写篇题解纪念一下。
特殊性质 A:所有 p_i=0
考虑序列 dp,记
由于我们最终的形态是一个排列,每一种局面到最后都会对应唯一的一种状态,这样做就可以做到不重不漏。
正解
我们记第
由于题面保证存在的
如何解决?我们可以记录有多少个已填入的数大于
通过一些简单的计算可以得出有多少个小于
当然我们还需要知道填入的数之间的相对大小来进行转移。
如果一个数小于
如果一个数大于
当
这时我们需要钦定
至此我们发现所有的转移都可以使用前缀和优化,时间复杂度
具体实现见代码。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=505;
int p[N],w[N];
int C[N][N];
int f[2][N][N],g[2][N][N];
//f:当前填的数比l_i大 g:当前填的数比l_i小
//第i个数 存在j个数比lim_i大 f:当前数的排名 g:比当前数还小的能填的数的个数
int num[N],L[N],R[N];
int sum[N];
int ascend(int c,int n,int m,vector<int> P,vector<int> W)
{
const int MOD=m;
auto add=[&](int &x,const int &y)
{x=x+y>=MOD?x+y-MOD:x+y;};
for(int i=1;i<=n;i++)
p[i]=P[i-1],w[i]=(W[i-1]-1+MOD)%MOD;
for(int i=1;i<=n;i++)
{
if(p[i]) L[i]=p[i];
else L[i]=L[i-1];
R[i]=R[i-1]+(p[i]==0);
num[i]=L[i]-i;
// cout<<R[i]<<" "<<lim[i]<<'\n';
}
C[0][0]=1;
for(int i=1;i<=n;i++)
{
C[i][0]=1,C[i][1]=i;
for(int j=2;j<=i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
}
memset(f,0,sizeof f);
memset(g,0,sizeof g);
if(p[1]) g[1][0][p[1]-1]=1;
else f[1][1][1]=1;
for(int i=2;i<=n;i++)
{
int now=i&1,pre=now^1;
memset(f[now],0,sizeof f[now]);
memset(g[now],0,sizeof g[now]);
if(p[i])
{
for(int j=0;j<=R[i];j++)
{
for(int k=0;k<=j+1;k++) sum[k]=0;
for(int k=0;k<=num[i-1]+j;k++) add(sum[j+1],g[pre][j][k]);
for(int k=j;k;k--)
sum[k]=(sum[k+1]+f[pre][j][k])%MOD;
for(int k=0;k<=j;k++)
{
int les=p[i]-i+k;
if(j-k>p[i]-L[i-1]-1) continue;
int xs=C[p[i]-L[i-1]-1][j-k];
add(g[now][k][les],1ll*xs*((sum[1]+1ll*sum[k+1]*w[i-1]%MOD)%MOD)%MOD);
}
}
}
else
{
for(int j=0;j<=R[i];j++)
{
int lim=num[i]+j;
if(lim+1<0) continue;
//g -> g/f
for(int k=0;k<=lim+1;k++) sum[k]=0;
for(int k=0;k<=lim+1;k++)
sum[k]=((k?sum[k-1]:0)+g[pre][j][k])%MOD;
for(int k=0;k<=lim;k++)
add(g[now][j][k],(sum[lim+1]+1ll*sum[k]*w[i-1])%MOD);
if(j!=R[i]) for(int k=1;k<=j+1;k++)
add(f[now][j+1][k],1ll*sum[lim+1]*(w[i-1]+1)%MOD);
//f -> g/f
for(int k=0;k<=j+1;k++) sum[k]=0;
for(int k=j;k;k--)
sum[k]=(sum[k+1]+f[pre][j][k])%MOD;
for(int k=0;k<=lim;k++)
add(g[now][j][k],sum[1]);
if(j!=R[i]) for(int k=1;k<=j+1;k++)
add(f[now][j+1][k],(sum[1]+1ll*sum[k]*w[i-1])%MOD);
}
}
}
int res=g[n&1][n-L[n]][0];
for(int i=1;i<=n-L[n];i++)
add(res,f[n&1][n-L[n]][i]);
return res;
}