ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Re:从爆零开始的预设型DP考题——我们不生产DP,我们只是DP的搬运工(缓更)

2022-07-24 19:31:51  阅读:131  来源: 互联网

标签:爆零 int 位置 贡献 Re 填数 MAXN DP dp


题面

题面在这里

有的没的

预设型dp的意思大概是 枚举当前放哪个数。
搬运工系列都是计数dp。


First

dp当然要从设立dp数组开始。

定义状态 \(dp[i][j][k]\) 表示 填到位置 \(i\) 、还有 \(j\) 个位置可以填数、贡献总和为 \(k\) 的方案数。

假定从小到大填数,对于一个数 \(x\) ,分类讨论:

  1. 如果 \(x\) 的左右在之后的操作中都被填上了数,那它左右的数都比它大,所以当前的 \(x\) 不一定会产生贡献。

  2. 如果之后的操作仅在 \(x\) 的一边填上了数,那 \(x\) 仍比它另一边的数大,所以贡献是 \(x\) 。

  3. 如果之后的操作没有在 \(x\) 的任意一边填上数, \(x\) 仍比它两边都大,贡献为 \(2x\) 。

同理,来推新加入的数:

  1. 如果新填入的点在数列的两端,并且空出相邻的一个位置,能填数的位置 \(j + 1\)

Second


Third

首先先转化一下,可以固定排列 \(B\) ,因为一个排列 \(B\) 只会对应一个排列 \(A\) 。

排列 \(B\) 的方案数有 \(n!\) 种,所以只要最后对答案乘一个 \(n!\) 就行了。

不妨设排列 \(B\) 为 \(1\) 到 \(n\) 。

然后分类讨论:

  1. 位置 \(i\) 不填数:
    贡献不变,空出的位置数 \(j + 1\) 。
    从 \(i - 1\) 位置直接转移。

    \[dp[i][j + 1][k] += dp[i - 1][j][k] \]

  2. 把 \(i\) 插入到之前的 \(j\) 个位置中的一个:
    插入了该位置,空的位置数 \(j --\) ,但位置 \(i\) 也空出来了, \(j++\) ,所以, \(j\) 不变。
    因为 \(B_{j} = j\) ,并且 \(i > j\) ,所以 \(i\)会产生贡献,贡献改变为 \(k + i\) 。
    再者一共有 \(j\) 个选点,可乘法原理得。

    \[dp[i][j][k + i] += dp[i - 1][j][k] * j \]

  3. 从之前没用的 \(j\) 中选一个插到位置 \(i\) :
    \(i\) 向前跳了 \(1\) ,\(j++\) 。但位置 \(i\) 又被填上了, \(j\) 不变。
    同理 \(B_{i} = i\) ,\(A_{i} = j\) , \(i > j\) ,所以 \(i\) 会产生贡献,贡献改变为 \(k + i\) 。
    同样,有 \(j\) 个数待选,乘法原理得。

    \[dp[i][j][k + i] += dp[i - 1][j][k] * j \]

  4. 把 \(i\) 填到位置 \(i\) :
    这 \(j\) 肯定不变。
    则 \(A_{i} = B_{i}\),\(i\) 会产生贡献,贡献改变为 \(k + i\) 。
    方案仍直接转移。

    \[dp[i][j][k + i] += dp[i - 1][j][k] * j \]

  5. 既把 \(i\) 插入到之前的 \(j\) 个位置,也从 \(j\) 中选一个数填到位置 \(i\) :
    结合上边说的情况2,3,可知 \(j\) 仍然不变。
    两个 \(i\) 都产生贡献,贡献改变为 \(k + i + i\) 。
    同样,乘法原理。

    \[dp[i][j - 1][k + i + i] += dp[i - 1][j][k] * j * j \]

当然,情况2到4可以合并。

\[dp[i][j][k + i] += dp[i - 1][j][k] * (2 * j + 1) \]

最后的答案就是

\[\sum_{i = k}^{n * n} dp[n][0][i] \]

Code

#include<cstdio>
#include<algorithm>

#define LL long long

using namespace std;

const int MAXN = 55;
const int Mod = 998244353;
int n, m;
LL dp[MAXN][MAXN][MAXN * MAXN];
//dp[i][j][k] 表示填到了数字 i, i 之前有 j 个位置没有填, 当前产生的价值为 k 
LL ans;

int main(){
    scanf("%d%d", &n, &m);

    dp[0][0][0] = 1;
    dp[1][0][1] = 1;
    dp[1][1][0] = 1;
    for(register int i = 2; i <= n; i++){
        for(register int j = 0; j <= i - 1; j++){ //到i最多有i - 1个位置没有填数
            for(register int k = 0; k <= i * i; k++){ //贡献最大的话是i * i
                if(!dp[i - 1][j][k]) continue; //如果为0的话肯定是没法达成这种情况,没法向后续转移 
                dp[i][j + 1][k] = (dp[i][j + 1][k] + dp[i - 1][j][k]) % Mod;
                dp[i][j][k + i] = (dp[i][j][k + i] + dp[i - 1][j][k] * j) % Mod;
                dp[i][j][k + i] = (dp[i][j][k + i] + dp[i - 1][j][k] * j) % Mod;
                dp[i][j][k + i] = (dp[i][j][k + i] + dp[i - 1][j][k]) % Mod;

                if(!j) continue;
                dp[i][j - 1][k + i + i] = (dp[i][j - 1][k + i + i] + dp[i - 1][j][k] * j * j) % Mod; 
            }
        }
    }

    for(register int i = m; i <= n * n; i++)
        ans = (ans + dp[n][0][i]) % Mod;
    for(register int i = 2; i <= n; i++)
        ans = 1LL * ans * i % Mod;

    printf("%lld", ans);

    return 0;
}

标签:爆零,int,位置,贡献,Re,填数,MAXN,DP,dp
来源: https://www.cnblogs.com/TSTYFST/p/16515258.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有