1. 程式人生 > >LOJ 2567: 洛谷 P3643: bzoj 4584: 「APIO2016」劃艇

LOJ 2567: 洛谷 P3643: bzoj 4584: 「APIO2016」劃艇

合數 clas 其他 要求 ans 方案 數組 type 兩種

題目傳送門:LOJ #2249。

題意簡述:

\(n\) 個位置,第 \(i\) 個位置可以填在 \([a_i,b_i]\)\(1\le a_i\le b_i\le 10^9\))之間的整數,也可以填 \(0\)

如果第 \(i\) 個位置填了非 \(0\) 的數,則這個數必須大於之前所有位置(\(1\)\(i-1\) 的位置)上的數。

至少要有一個位置填上非 \(0\) 的數。問最終有幾種填數方案,兩種填數方案不同當且僅當某個位置上填的數不同。

題解:

要求即為選出一些位置填數,並且形成嚴格遞增序列。

發現把所有區間離散化,可以組成 \(\mathcal{O}(n)\) 個連續段,每個連續段中的數的性質是相同的。

因為 \(n\) 不大(只有 \(500\)),考慮 DP。令 \(\mathrm{f}[i][j]\) 表示前 \(i\) 個位置,第 \(i\) 個位置填了在段 \(j\) 中的數的方案數。

顯然若 \([a_i,b_i]\) 不包含段 \(j\),則 \(\mathrm{f}[i][j]=0\)。對於其他的情況,考慮如何轉移,假設上一個填了在小於 \(j\) 的段中的數的位置是 \(k\),則有 \(\mathrm{f}[i][j]\)\(\mathrm{f}[k][j']\) 轉移,其中 \(0\le k<i\)\(1\le j'<j\)

轉移的具體形式是:假設位置 \((k,i]\)

中一共有 \(pos\) 個位置(包括 \(i\) 本身)包含段 \(j\),而段 \(j\) 的長度為 \(len\),則 \(\mathrm{f}[k][j']\) 貢獻 \(\sum_{x=1}^{pos}\binom{pos-1}{x-1}\binom{len}{x}\) 倍給 \(\mathrm{f}[i][j]\)。這是因為我們枚舉 \(pos\) 個位置中填了 \(x\) 個位置,但是第 \(i\) 個位置必須填,所以乘上 \(\binom{pos-1}{x-1}\),然後 \(len\) 個可行數中選出 \(x\) 個從小到大依次填入,所以再乘上 \(\binom{len}{x}\)
。化簡一下系數:\(\sum_{x=1}^{pos}\binom{pos-1}{x-1}\binom{len}{x}=\sum_{x}\binom{len}{x}\binom{pos-1}{pos-x}=\binom{len+pos-1}{pos}\)。式子可以這樣理解:枚舉 \(x\),從 \(len\) 個數中選出 \(x\) 個數,再從 \(pos-1\) 個數中選出 \(pos-x\) 個數,這與直接從 \(len+pos-1\) 個數中選出 \(pos\) 個數等價。

那麽我們有 \(\mathrm{f}[i][j]=\sum_{k=0}^{i-1}\binom{len+pos-1}{pos}\sum_{j'=0}^{j-1}\mathrm{f}[k][j']\)。邊界是 \(\mathrm{f}[0][0]=1\)\(\mathrm{f}[0][j]=0\)\(1\le j\))和 \(\mathrm{f}[i][0]=0\)\(1\le i\))。

再利用前綴和優化,把第二維做前綴和,把轉移變成 \(\mathcal{O}(n)\) 的就好了,轉移內的組合數可以 \(\mathcal{O}(n)\) 預處理。

還可以滾動數組一下,空間就是 \(\mathcal{O}(n)\) 的了,不過沒必要。

#include <cstdio>
#include <algorithm>

typedef long long LL;
const int Mod = 1000000007;
const int MN = 505;

int N, lb[MN], rb[MN], lp[MN * 2], len[MN * 2], cnt;
int Inv[MN], C[MN], f[MN], Ans;

int main() {
    scanf("%d", &N);
    for (int i = 1; i <= N; ++i)
        scanf("%d%d", &lb[i], &rb[i]),
        lp[++cnt] = lb[i],
        lp[++cnt] = rb[i] + 1;
    std::sort(lp + 1, lp + cnt + 1);
    cnt = std::unique(lp + 1, lp + cnt + 1) - lp - 2;
    for (int i = 1; i <= cnt; ++i) len[i] = lp[i + 1] - lp[i];
    for (int i = 1; i <= N; ++i)
        lb[i] = std::lower_bound(lp + 1, lp + cnt + 1, lb[i]) - lp,
        rb[i] = std::lower_bound(lp + 1, lp + cnt + 1, rb[i] + 1) - lp - 1;
    Inv[1] = 1, C[0] = 1, f[0] = 1;
    for (int i = 2; i <= N; ++i) Inv[i] = (LL)(Mod - Mod / i) * Inv[Mod % i] % Mod;
    for (int i = 1; i <= cnt; ++i) {
        int l = len[i];
        for (int j = 1; j <= N; ++j)
            C[j] = (LL)C[j - 1] * (l + j - 1) % Mod * Inv[j] % Mod;
        for (int j = N; j >= 1; --j) if (lb[j] <= i && i <= rb[j]) {
            for (int k = j, a = 0; k >= 1; --k) {
                if (lb[k] <= i && i <= rb[k]) ++a;
                f[j] = (f[j] + (LL)f[k - 1] * C[a]) % Mod;
            }
        }
    }
    for (int i = 1; i <= N; ++i) Ans = (Ans + f[i]) % Mod;
    printf("%d\n", Ans);
    return 0;
}

LOJ 2567: 洛谷 P3643: bzoj 4584: 「APIO2016」劃艇