非确定性有穷状态决策自动机练习题Vol.3 D. Dp搬运工3 计数DP

非确定性有穷状态决策自动机练习题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;
}
posted @ 2020-08-19 18:51  liuchanglc  阅读(179)  评论(2编辑  收藏  举报