eastcloud
2024-11-18 19:23:15
首先考虑怎么刻画这个冒泡排序的过程,手玩一下可以发现,一轮冒泡排序把所有前缀最大值移到了下一个前缀最大值之前,且我们只做了这样的操作。
由于题目还要求了每个位置必须放什么数,因此我们平常用于求解排列个数的方法例如从小到大插入或是怎么计数一下都不太方便,因此不妨就从冒泡排序的过程入手设计思路。
在这里,前缀最大值是比较重要的,因此你可以考虑记录一下在我们目前构造出来的这个排列中,做一轮冒泡排序移到最前面的是什么数,设
怎么转移呢?先考虑一下题目给的部分数被钦定了的限制,考虑我们怎么满足这个限制:这个数要么从前面被移过来,要么从后面被一次交换换过来,要么不动,而这些在我们转移到第
还要注意一个问题:如果后面需要放的数被前面放了怎么办?这好办,直接前面钦定不要填就好了,设
若
若
最后别忘了前面向比
int main(){
int n=read();
for(int i=1;i<=n;i++){
q[i]=read();
if(q[i]!=-1)buc[q[i]]++;
}
if(q[n]!=-1 && q[n]!=n){write(0);return 0;}
for(int i=1;i<=n;i++)f[1][i]=1;
for(int i=1;i<n;i++){
if(q[i]!=-1)buc[q[i]]--;
for(int j=1;j<=n;j++)sum[j]=sum[j-1]+buc[j];
for(int j=1;j<=n;j++){
if(!f[i][j])continue;
if(q[i]!=-1){
if(q[i]==j) for(int k=j+1;k<=n;k++)Add(f[i+1][k],f[i][j]);
else if(q[i]<j)Add(f[i+1][j],f[i][j]);
}
else{
int tot=j-1-(i-1)-sum[j-1];
if(tot<=0)continue;
Add(f[i+1][j],mul(f[i][j],tot));
}
}
if(q[i]==-1){
int S=0;
for(int j=1;j<=n;j++){
Add(f[i+1][j],S);
if(!buc[j])Add(S,f[i][j]);
}
}
}
write(f[n][n]);
return 0;
}