非確定性有窮狀態決策自動機練習題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] \times (j \times 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] \times j \times 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\n",ans);
return 0;
}