AtCoder Beginner Contest 282 G

2023-01-13 21:00:14

套路題

題意

求有多少個 \(1\)\(n\) 的排列滿足恰有 \(k\) 對在排列中相鄰的數滿足前小於後

\(2 \leq n \leq 500, 0 \leq k \leq (n - 1)\)

思路

f[i][j][k] 表示已經放置了前 i 個數, 放置的第i個數是前i個數中第j大的($ 1\leq\(`j`\)\leq$i),已放置的前i個數形成的所有排列滿足恰有 k 對在排列中相鄰的數滿足前小於後的排列數量。

放置第i+1個數時,第i+1個數是前i+1個數中第j大的,第i個數是嚴格小於前i個數中第j大的,會為排列增加一對相鄰的數滿足前小於後,第i個數是大於等於前i個數中第j大的,不會為排列增加一對相鄰的數滿足前小於後,轉移方程為:

\[f_{(i + 1) j k} = \sum_{x = 1}^{j - 1}f_{i x (k-1)} + \sum_{x=j}^{i}f_{ixk} \]

顯然,後面的和式可以通過字首和優化的。

時間複雜度為\(O(n^2k)\)

G - Similar Permutation

傳送門

題意

\(1\)\(n\)的排列\(A\)\(B\)的相似度為\(k\)的數量。

相似度計算:\(k = \sum_{i = 2}^{n}[(A_i - A_{i-1})(B_i - B_{i-1}) > 0]\) (\([X] = 1, X 為真,[X] = 0, X為假\))。

\(2 \leq n \leq 100, 0 \leq k \leq (n - 1)\)

思路

與前一道題相比,這一題只是增加了一維狀態。

f[i][a][b][k] 表示排列\(A\),\(B\)已經放置了前 i 個數, 排列\(A\)放置的第i個數在排列\(A\)中是第a大的,排列\(B\)放置的第i個數在排列\(B\)中是第b大的,此時相似度為\(k\)的排列數量。

轉移方程為:

\[f_{(i+1)abk} = \sum_{x = 1}^{a - 1}\sum_{y = 1}^{b - 1} f_{ixy(k-1)} + \sum_{x = a}^{i}\sum_{y = b}^{i} f_{ixy(k-1)} + \sum_{x = 1}^{a - 1}\sum_{y = b}^{i} f_{ixyk} + \sum_{x = a}^{i}\sum_{y = 1}^{b - 1} f_{ixyk} \]

和式同樣可以使用字首和來優化。

時間複雜度為\(O(n^4)\)

程式碼

int pre[107][107][107], f[107][107][107];
void solve_problem() {
    int n, m, P;

    std::cin >> n >> m >> P;

    auto add = [&](int a, int b) -> int {
        a += b;
        if ( a >= P ) a -= P;
        return a;
    };
    auto sub = [&](int a, int b) -> int {
        a -= b;
        if ( a < 0 ) a += P;
        return a;
    };
    auto sum = [&](int n, int x1, int y1, int x2, int y2) -> int {
        if (n < 0) return 0;
        return add(sub(sub(pre[n][x2][y2], pre[n][x2][y1 - 1]), pre[n][x1 - 1][y2]), pre[n][x1 - 1][y1 - 1]);
    };

    for (int i = 0; i <= n; i++) 
        for (int j = 0; j <= n; j++) 
            for (int h = 0; h <= n; h++) 
                pre[i][j][h] = f[i][j][h] = 0;
    
    f[0][1][1] = 1;

    for (int i = 1; i <= n; i++) {
        for (int k = 0; k <= i + 1; k++) {
            for (int a = 1; a <= i; a++) {
                for (int b = 1; b <= i; b++) {
                    pre[k][a][b] = add(pre[k][a][b - 1], f[k][a][b]);
                }
            }
            for (int b = 1; b <= i; b++) {
                for (int a = 1; a <= i; a++) {
                    pre[k][a][b] = add(pre[k][a][b], pre[k][a - 1][b]);
                }
            }
        }
        for (int k = 0; k <= i + 1; k++) {
            for (int a = 1; a <= i + 1; a++) {
                for (int b = 1; b <= i + 1; b++) {
                    f[k][a][b] = add(
                                    add(sum(k - 1, 1, 1, a - 1, b - 1), sum(k - 1, a, b, i, i)), 
                                    add(sum(k, 1, b, a - 1, i), sum(k, a, 1, i, b - 1))
                                    );
                }
            }
        }
    }
    std::cout << sum(m, 1, 1, n, n) << "\n";
}