AGC030FPermutation and Minimum(DP)

Posted zzy2005

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AGC030FPermutation and Minimum(DP)相关的知识,希望对你有一定的参考价值。

题目链接

题解

首先可以想到分组后,去掉两边都填了数的组。
然后就会剩下((-1,-1))((-1,x))((x,-1))这两种情况
因为是最小值序列的情况数,我们可以考虑从大到小填数,一个组填完了最小值就是当前填的数
(f[i][j][k])表示已经填到数(i)剩余(j+k)个填了一个数的组,其中有(j)个组是第一种情况填出来的,(k)个是第二种情况来的
分情况转移
如果这个数属于第一种情况,那么可以转移到:
(f[i][j+1][k]:)填到一个((-1,-1))
(f[i][j-1][k]:)和之前填了一个数的((-1,-1))完成一组
(f[i][j][k-1]:)((x,-1))完成一组
如果属于第二种情况,那么可以转移到:
(f[i][j][k+1]:)不是最小值
(f[i][j-1][k]:)是最小值
最后因为((-1,-1))是无序的,所以答案要乘上一个阶乘

Code

int cnt1 = 0, cnt2 = 0;
    for (int i = 1; i < 2 * n; i += 2) {
        if (a[i] == a[i + 1])
            cnt2++;
        else {
            if (a[i] > a[i + 1]) swap(a[i], a[i + 1]);
            if (a[i] == -1) cnt1++, flg[a[i + 1]] = 1;          
            else vis[a[i]] = vis[a[i + 1]] = 1;
        }
    }
    int m = 0;
    f[0][0][0] = 1;
    for (int i = 2 * n; i >= 1; i--)
        if (!vis[i]) {
            m++;
            if (!flg[i]) {
                for (int j = 0; j <= cnt1 + cnt2; j++)
                    for (int k = 0; k <= cnt1; k++) {
                        pls(f[m][j + 1][k], f[m - 1][j][k]);
                        if (j) pls(f[m][j - 1][k], f[m - 1][j][k]);
                        if (k) pls(f[m][j][k - 1], 1ll * k * f[m - 1][j][k] % Mod);
                    }
            }
            else {
                for (int j = 0; j <= cnt1 + cnt2; j++)
                    for (int k = 0; k <= cnt1; k++) {
                        pls(f[m][j][k + 1], f[m - 1][j][k]);
                        if (j) pls(f[m][j - 1][k], f[m - 1][j][k]);
                    }
            }
        }
    int ans = f[m][0][0];
    for (int i = 1; i <= cnt2; i++)
        ans = 1ll * ans * i % Mod;

以上是关于AGC030FPermutation and Minimum(DP)的主要内容,如果未能解决你的问题,请参考以下文章

AtCoder AGC030B Tree Burning

AtCoder Grand Contest 030 (AGC030) C - Coloring Torus 构造

AtCoder AGC030E Less Than 3

AGC 030 B - Tree Burning

AGC 030 B - Tree Burning 结论+枚举

AGC 030B.Tree Burning(贪心)