【題解】洛谷P4707重返現世

  在跨年的晚上玩手機被媽媽罵了趕來寫題……嗚嗚嗚……可是A題了仍是很開心啦,起碼沒有把去年的題目留到明年去作ヾ(◍°∇°◍)ノ゙也祝你們2019快樂!c++

  這題顯然的 kth min-max 容斥就不說了,不會的仍是百度吧……記錄一下後面的 dp。感受挺強強的,%題解……spa

  首先,min - max 容斥的公式爲 : \(max_{K}(S) = \sum_{T\subseteq S}(-1)^{|T|-K}\binom{|T|-1}{K-1}min(T)\)code

  可是最後面的 \(min(T)\) 顯然不能 \(2 ^ {n}\) 枚舉,但又是非線性的求和。因此咱們須要一點不同的dp……考慮到 \(n - K <= 10\),實際上也就是說在上面公式中出現的 \(\binom{|T| - 1}{K - 1}\) 中的 \(K - 1\) 最大不會超過 11。從這個地方入手,設 在前 \(x\) 個元素組成的集合中, \(g_{x, i, j}\) 爲全部 \(min(T) = j\) 且 \(|T| = i\) 的子集的方案數,blog

而\(f_{x, j, k} = \sum_{i = 1}(-1)^{i - k}\binom{i - 1}{k - 1}*g_{x, i, j}\)繼承

考慮向集合中加入第 \(i\) 個元素,不加入的直接繼承上一次的。get

加入的則須要分析一下(下面的就只討論包含第 \(i\) 個元素的狀況)it

考慮從 \(f_{x - 1, j - v, k - 1}\) 轉移過來(\(v\) 爲 \(p[i]\))class

分析:\(f_{x - 1, j - v, k - 1} = \sum_{i = 1}(-1)^{i - k + 1}\binom{i - 1}{k - 2}*g_{x - 1, i, j - v}\)百度

爲了便於觀察,咱們儘可能把 \(f_{x, j, k}\) 也寫成同樣的形式im

\(f_{x, j, k} = \sum_{i = 1}(-1)^{i - k + 1}\binom{i}{k - 1}*g_{x - 1, i, j - v}\)

由於咱們有 \(\binom{n}{m} = \binom{n - 1}{m}+\binom{n - 1}{m - 1}\)

因此 \(f_{x, j, k} - f_{x - 1, j- v, k - 1} = \sum_{i = 1}(-1)^{i - k + 1}\binom{i - 1}{k - 1}*g_{x, i, j - v} = -f_{x - 1, j - v, k}\)

因此,完整的式子是:

\(f_{x, j, k} = f_{x - 1, j, k} + f_{x - 1, j - v, k - 1} - f_{x - 1, j - v, k}\)

  這樣就能夠愉快地遞推啦。不過還有一個小小的細節,就是邊界的問題。咱們只須要每次保存 \(f_{x, 0, 0} = 1\) 便可,由於會從 \(0\) 轉移出去的當且僅當 \(v = j\) 即集合中僅有一個元素時。此時顯然有 \(f_{x, p[x], 1} = 1\)。

#include <bits/stdc++.h>
using namespace std;
#define maxn 2005
#define maxm 20000
#define mod 998244353 
#define int long long
int n, K, m, f[2][maxm][20];
int ans, now, pre, p[maxn];
int inv[maxm], finv[maxm], fac[maxm];

int read()
{
    int x = 0, k = 1;
    char c; c = getchar();
    while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * k;
}

void Up(int &x, int y) { x = (x + y) % mod; if(x < 0) x += mod; }
void init()
{
    fac[0] = 1, inv[0] = inv[1] = 1; finv[0] = 1;
    for(int i = 1; i < maxm; i ++) fac[i] = fac[i - 1] * i % mod;
    for(int i = 2; i < maxm; i ++) inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    for(int i = 1; i < maxm; i ++) finv[i] = finv[i - 1] * inv[i] % mod;
}

int C(int n, int m)
{
    if(n < m || n < 0 || m < 0) return 0;
    return fac[n] * finv[m] % mod * finv[n - m] % mod;
}

int Qpow(int x, int timer)
{
    int base = 1;
    for(; timer; timer >>= 1, x = x * x % mod)
        if(timer & 1) base = base * x % mod;
    return base;
}

void DP()
{
    now = 1, pre = 0; f[pre][0][0] = 1;
    for(int i = 1; i <= n; i ++, swap(now, pre), f[pre][0][0] = 1)
        for(int j = 1; j <= m; j ++)
            for(int k = 1; k <= K; k ++)
            {
                f[now][j][k] = f[pre][j][k]; 
                if(j < p[i]) continue;
                Up(f[now][j][k], f[pre][j - p[i]][k - 1]);
                Up(f[now][j][k], -f[pre][j - p[i]][k]);
            }
}

signed main()
{
    n = read(), K = read(), m = read(); init();
    for(int i = 1; i <= n; i ++) p[i] = read();
    K = n - K + 1; DP(); 
    for(int i = 1; i <= m; i ++)
        Up(ans, f[pre][i][K] * m % mod * inv[i] % mod);
    printf("%lld\n", ans);
    return 0;
}
相關文章
相關標籤/搜索