2021牛客多校第一場 I題(DP)


題意

給定一個長度為 \(n(n<=5000)\)排列,兩個人輪流從這個序列中選擇一個數,要求當前回合此人選擇的數大於任意一個已經被選擇的數,並且該數在數組中的位置 \(i\) 與此人上一次選擇的數在數組中的位置 \(j\) 要滿足 \(i>j\),如果有多個數合法則等概率的從這些數中選一個。當沒有合法數時結束,問最終被選擇的數的期望個數。

分析

考慮 \(dp\) ,設 \(dp[x][y]\) 為當前輪到此人選數並且他上一次選了數 \(x\),另一個人選了數 \(y\) 開始到游戲結束時選擇的數的期望個數。則 \(dp[x][y] = inv[tot] * \sum_{i=1}^{tot} dp[y][a_i] + 1\)\(tot\) 為可選擇的數字的個數,\(a_{i}\) 為可選的數字。首先枚舉 \(y\) ,然后用前綴和處理一下即可 \(O(1)\) 完成轉移,再枚舉 \(x\),總復雜度 \(O(n^2)。\)

代碼

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
using ll = long long;
constexpr int N = 5005;
const int MOD = 998244353;

int T, n;
int p[N], c[N], sum[N], pos[N], inv[N];
int dp[N][N];

int qpow(int x, int k) {
    int ret = 1;
    while(k) {
        if(k & 1)  ret = (ll) ret * x % MOD;
        x = (ll) x * x % MOD;
        k >>= 1;
    }
    return ret;
}

int main() {
	scanf("%d", &n);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &p[i]);
        pos[p[i]] = i;
        inv[i] = qpow(i, MOD - 2);
    }
    for(int j = n; j > 0; --j) {
        for(int i = 0; i <= n; ++i)  c[i] = sum[i] = 0;
        for(int t = j + 1; t <= n; ++t) {
            c[pos[t]] = 1;
            sum[pos[t]] = dp[j][t];
        }
        for(int i = n - 1; i >= 0; --i) {
            c[i] += c[i + 1];
            sum[i] = (sum[i] + sum[i + 1]) % MOD;
        }
        for(int i = j - 1; i >= 0; --i) {
            int tot = c[pos[i]];
            int sm = sum[pos[i]];
            if(tot)  dp[i][j] = ((ll) inv[tot] * sm + 1) % MOD;
        }
    }
    int ret = 0;
    for(int i = 1; i <= n; ++i)  ret = (ret + dp[0][i]) % MOD;
    printf("%lld", ((ll) ret * inv[n] % MOD + 1) % MOD);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM