DP搬运工1
D. DP搬运工1
内存限制:512 MiB 时间限制:1000 ms 输入文件:D.in 输出文件:D.out
题目类型:传统 评测方式:文本比较
题目描述
给你n,k,求有多少个1到n的排列,满足相邻两个数的max的和不超过。
输入格式
一行两个整数n,k。
输出格式
一行一个整数ans表示答案mod 998244353。
样例
样例输入1
4 10
样例输出1
16
样例输入2
10 66
样例输出2
1983744
数据范围与提示
有50个测试点,第i个测试点为n=i,k<=$n^2$。
思路:
题外话:一般来说,会先设f[i][j]表示前i个数max的和为j的方案数,但转移时因为不知道第i+1个数放在哪,所以会有后效性,不可行。
题解:根据上面错误的思路,可以加以为则$f[i][j][k]$表示前i个数,在放入前$i$个数后还有$j$个位置,$max$和为k的方案数。
当i+1要放在这些数之间时,若i+1旁边能放两个数,则贡献为0;若i+1旁边能放一个数,贡献为i+1;若一个数也不能放,则贡献为i+1+i+1;
当i+1放两边时,同上
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 typedef long long ll;//注意强转long long 6 const int maxn=50+5,mod=998244353; 7 int f[maxn][maxn*maxn],g[maxn][maxn*maxn]; 8 int main(){ 9 int n,k; 10 scanf("%d%d",&n,&k); 11 f[0][0]=1;//初始化是针对i==1时 12 for(int i=2;i<=n;i++){//注意i应从2开始,因为初始化针对1 13 memcpy(g,f,sizeof(f));//g保存i-1的信息 14 memset(f,0,sizeof(f)); 15 for(int j=0;j<=n-i+1;j++){//因为g保存i-1的信息,j<=n-(i-1) 16 for(int m=0;m<=k;m++){ 17 int x=g[j][m]; 18 if(!x) continue; 19 if(j)f[j][m+i]=(ll)(f[j][m+i]+(ll)2*x*j)%mod; 20 if(j)f[j+1][m]=(ll)(f[j+1][m]+(ll)x*j)%mod; 21 if(j)f[j-1][m+i+i]=(ll)(f[j-1][m+i+i]+(ll)x*j)%mod; 22 f[j][m+i]=(ll)(f[j][m+i]+(ll)2*x)%mod; 23 f[j+1][m]=(ll)(f[j+1][m]+(ll)2*x)%mod; 24 } 25 } 26 } 27 int ans=0; 28 for(int i=0;i<=k;i++) ans=(ans+f[0][i])%mod; 29 printf("%d\n",ans); 30 return 0; 31 }