非确定性有穷状态决策自动机练习题Vol.3 D. Dp搬运工3
题目描述
给定两个长度为 (n) 的排列,定义 (magic(A,B)=∑_{i=1}^nmax(Ai,Bi)) 。
现在给定 (n),(K) 问有多少对 ((A,B)) 满足 (magic(A,B)≥K)。
分析
首先转化一下,我们固定排列 (B) 为 $1∼n $,最后答案乘个 (n!) 就好了
我们设 (f[i][j][k]) 为 考虑到第 (i) 个位置,(i) 之前有 (j) 个位置没有填,当前产生的价值为 (k) 的方案数
我们可以选择在 (i) 的位置不填数,此时直接转移即可
(f[i][j+1][k]=f[i][j+1][k]+f[i-1][j][k])
我们可以把当前的 (i) 插入到之前没有填过的 (j) 个位置或者从之前没有用过的 (j) 个数中选择一个填到 (i) 所在的位置,还可以把数字 (i) 填入 (i) 的位置
此时的转移方程为
(f[i][j][k+i]=f[i][j][k+i]+f[i-1][j][k] imes (j imes 2+1))
我们还可以既把当前的 (i) 插入到之前没有填过的 (j) 个位置又从之前没有用过的 (j) 个数中选择一个填到 (i) 所在的位置,此时
(f[i][j-1][k+i+i]=f[i][j-1][k+i+i]+f[i-1][j][k] imes j imes j)
代码
#include<cstdio>
#include<algorithm>
const int maxn=55;
const int mod=998244353;
long long f[maxn][maxn][maxn*maxn];
int n,k;
int main(){
freopen("D.in","r",stdin);
freopen("D.out","w",stdout);
scanf("%d%d",&n,&k);
f[1][0][1]=f[1][1][0]=1;
for(int i=2;i<=n;i++){
int maxj=std::min(i-1,n-i+1);
int maxk=i*i;
for(int j=0;j<=maxj;j++){
for(int k=0;k<=maxk;k++){
if(f[i-1][j][k]){
f[i][j+1][k]=(f[i][j+1][k]+f[i-1][j][k])%mod;
f[i][j][k+i]=(f[i][j][k+i]+f[i-1][j][k]*(j*2LL+1))%mod;
if(j) f[i][j-1][k+i+i]=(f[i][j-1][k+i+i]+f[i-1][j][k]*j*j*1LL)%mod;
}
}
}
}
long long ans=0;
for(int i=k;i<=n*n;i++){
ans=(ans+f[n][0][i])%mod;
}
for(int i=2;i<=n;i++){
ans=ans*1LL*i%mod;
}
printf("%lld
",ans);
return 0;
}