# 牛客挑战赛46_C题排列(前缀优化DP)
牛客挑战赛46_C题排列(前缀优化DP)
题意:给定超级逆序对为k,求长度为n的排列的方案数;
超级逆序对:满足 i<j 且 a[i]>a[j]+1 的二元组 (i,j);
题解:若题目是逆序对个数为k,对于从1到n的每一个数,从小至大的插入数组,都有(0,i-1)的安排方案,枚举每一个数,记忆化搜索一下即可,现在多了一个超级逆序对,即每个数最多贡献i-2的超级逆序对的个数,并且会因为不同的i-1的位置影响不同的转移。所以我们可以给dp加一维i-1的位置信息,并加一层循环即可。空间是n ^ 3,时间是n ^ 4。爆空间,且超时。空间考虑滚动数组优化,并且我们容易发现转移具有单调性,所以利用预处理前缀和优化dp,达到n ^ 2空间,n ^ 3时间。
#include<iostream>
#include<cstring>
using namespace std;
#define ll long long
const ll mod=998244353;
ll n,k,lin[507][507],dp[507][507],pre[507][507];
ll pow(ll x,ll n,ll mod){
ll res=1;
while(n>0){
if(n%2==1){
res=res*x;
res=res%mod;
}
x=x*x;
x=x%mod;
n>>=1;
}
return res;
}
ll inv(ll x){
return pow(x,mod-2,mod);
}
int main(){
cin>>n>>k;
lin[0][1]=1;
for(int i=2;i<=n;i++){
for(int j=0;j<=k;j++){
for(int c=1;c<i;c++){
pre[j][c]=(pre[j][c-1]+lin[j][c])%mod;
}
}
memset(dp,0,sizeof(dp));
for(int j=0;j<=k;j++){
for(int d=1;d<=i;d++){
dp[j][d]=(dp[j][d]+pre[j-i+d][d-1])%mod;
dp[j][d]=(dp[j][d]+(pre[j-i+d+1][i-1]-pre[j-i+d+1][d-1]+mod)%mod)%mod;
}
}
for(int j=0;j<=k;j++){
for(int d=1;d<=i;d++){
lin[j][d]=dp[j][d];
}
}
}
ll ans=0;
for(int i=1;i<=n;i++){
ans+=lin[k][i];
ans%=mod;
}
printf("%lld\n",inv(ans));
}