提出 #74718713


ソースコード 拡げる

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int MAXN = 1000005;
const int N_DP = 105;
const int mod = 998244353;

ll a[MAXN], b[MAXN], sum[MAXN];
ll p[MAXN];
int dp[N_DP][N_DP][N_DP];
int temp_dp[N_DP][N_DP][N_DP];
int next_dp[N_DP][N_DP][N_DP];
ll fact[N_DP], invfact[N_DP];
ll pw[N_DP][N_DP];

ll ksm(ll x, ll p) {
    ll res = 1, item = x % mod;
    while (p) {
        if (p & 1) res = res * item % mod;
        p >>= 1;
        item = item * item % mod;
    }
    return res;
}

void init() {
    fact[0] = 1; 
    invfact[0] = 1;
    for (int i = 1; i < N_DP; i++) {
        fact[i] = fact[i - 1] * i % mod;
        invfact[i] = ksm(fact[i], mod - 2);
    }
}

void solve() {
    int n, m;
    cin >> n >> m;
    int rn = n; // 记录原始总数
    
    for (int i = 1; i <= n; i++) cin >> a[i];
    sort(a + 1, a + 1 + n);

    // 离散化并记录前缀目标数目
    int k = 0;
    for (int i = 1; i <= n; i++) {
        if (i == 1 || a[i] != a[i - 1]) {
            k++;
            b[k] = a[i];
            sum[k] = 1;
        } else {
            sum[k]++;
        }
    }
    for (int i = 1; i <= k; i++) sum[i] += sum[i - 1];
    n = k; // n 更新为不同元素的个数

    b[n + 1] = m + 1;
    ll fm = ksm(m, mod - 2);
    
    // 提前计算好每个区间的命中概率
    for (int i = 1; i <= n; i++) p[i] = (b[i + 1] - b[i]) * fm % mod;
    p[0] = (b[1] - 1) * fm % mod;
    sum[0] = 0;

    // 预处理 EGF 的项 (p[i]^x / x!)
    for (int i = 0; i <= n; i++) {
        ll cur = 1;
        for (int x = 0; x <= rn; x++) {
            pw[i][x] = cur * invfact[x] % mod;
            cur = cur * p[i] % mod;
        }
    }

    memset(dp, 0, sizeof(dp));
    dp[0][0][rn] = 1; // 初始状态: 抽取0次,匹配0次,所有rn个元素均处于活跃等待状态

    // 逆序处理各个区间段
    for (int i = n; i >= 0; i--) {
        memset(temp_dp, 0, sizeof(temp_dp));
        
        // 1. 处理区间门槛(将活跃目标数量卡死在 sum[i] 以内)
        for (int C = 0; C <= rn; C++) {
            for (int M = 0; M <= C; M++) {
                for (int curr_k = 0; curr_k <= rn; curr_k++) {
                    if (dp[C][M][curr_k]) {
                        int next_k = min(curr_k, (int)sum[i]);
                        temp_dp[C][M][next_k] = (temp_dp[C][M][next_k] + dp[C][M][curr_k]) % mod;
                    }
                }
            }
        }

        memset(next_dp, 0, sizeof(next_dp));
        
        // 2. 在当前区间进行 x 次独立抽取
        for (int C = 0; C <= rn; C++) {
            for (int M = 0; M <= C; M++) {
                for (int curr_k = 0; curr_k <= sum[i]; curr_k++) {
                    ll val = temp_dp[C][M][curr_k];
                    if (!val) continue;

                    // 优化:将 x 分段,避免在内层循环做 min 判断
                    // 情况 A: x <= curr_k (所有抽取都转为了有效匹配)
                    for (int x = 0; x <= curr_k && C + x <= rn; x++) {
                        next_dp[C + x][M + x][curr_k - x] = (next_dp[C + x][M + x][curr_k - x] + val * pw[i][x]) % mod;
                    }
                    // 情况 B: x > curr_k (匹配已达上限,剩下的抽取全部打水漂)
                    for (int x = curr_k + 1; C + x <= rn; x++) {
                        next_dp[C + x][M + curr_k][0] = (next_dp[C + x][M + curr_k][0] + val * pw[i][x]) % mod;
                    }
                }
            }
        }
        memcpy(dp, next_dp, sizeof(dp));
    }

    // 统计各个匹配次数的概率并输出
    for (int i = 0; i <= rn; i++) {
        ll ans = 0;
        for (int curr_k = 0; curr_k <= rn; curr_k++) {
            ans = (ans + dp[rn][i][curr_k]) % mod;
        }
        // 乘回 n! (即 rn!) 恢复成排列总权重
        ans = ans * fact[rn] % mod; 
        cout << ans << " ";
    }
    cout << "\n";
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    init(); // 初始化阶乘表
    
    int t = 1;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

提出情報

提出日時
問題 C - Greedy Customers 2
ユーザ zhishengie
言語 C++23 (GCC 15.2.0)
得点 700
コード長 4266 Byte
結果 AC
実行時間 147 ms
メモリ 17428 KiB

ジャッジ結果

セット名 Sample All
得点 / 配点 0 / 0 700 / 700
結果
AC × 1
AC × 27
セット名 テストケース
Sample 00_sample_00.txt
All 00_sample_00.txt, 01_handmade_00.txt, 01_handmade_01.txt, 01_handmade_02.txt, 01_handmade_03.txt, 02_random_00.txt, 02_random_01.txt, 02_random_02.txt, 02_random_03.txt, 02_random_04.txt, 02_random_05.txt, 02_random_06.txt, 02_random_07.txt, 02_random_08.txt, 02_random_09.txt, 02_random_10.txt, 02_random_11.txt, 02_random_12.txt, 02_random_13.txt, 02_random_14.txt, 02_random_15.txt, 02_random_16.txt, 02_random_17.txt, 02_random_18.txt, 02_random_19.txt, 02_random_20.txt, 02_random_21.txt
ケース名 結果 実行時間 メモリ
00_sample_00.txt AC 17 ms 17284 KiB
01_handmade_00.txt AC 9 ms 17188 KiB
01_handmade_01.txt AC 9 ms 17284 KiB
01_handmade_02.txt AC 143 ms 17248 KiB
01_handmade_03.txt AC 147 ms 17248 KiB
02_random_00.txt AC 107 ms 17312 KiB
02_random_01.txt AC 146 ms 17048 KiB
02_random_02.txt AC 140 ms 17284 KiB
02_random_03.txt AC 141 ms 17280 KiB
02_random_04.txt AC 144 ms 17184 KiB
02_random_05.txt AC 141 ms 17176 KiB
02_random_06.txt AC 143 ms 17176 KiB
02_random_07.txt AC 141 ms 17304 KiB
02_random_08.txt AC 142 ms 17296 KiB
02_random_09.txt AC 144 ms 17264 KiB
02_random_10.txt AC 142 ms 17428 KiB
02_random_11.txt AC 142 ms 17280 KiB
02_random_12.txt AC 142 ms 17336 KiB
02_random_13.txt AC 142 ms 17324 KiB
02_random_14.txt AC 140 ms 17172 KiB
02_random_15.txt AC 30 ms 17324 KiB
02_random_16.txt AC 82 ms 17176 KiB
02_random_17.txt AC 8 ms 17172 KiB
02_random_18.txt AC 92 ms 17284 KiB
02_random_19.txt AC 41 ms 17264 KiB
02_random_20.txt AC 73 ms 17284 KiB
02_random_21.txt AC 51 ms 17264 KiB