題目傳送門:LOJ #2567。數組
有 $n$ 個位置,第 $i$ 個位置能夠填在 $[a_i,b_i]$ ($1\le a_i\le b_i\le 10^9$)之間的整數,也能夠填 $0$。優化
若是第 $i$ 個位置填了非 $0$ 的數,則這個數必須大於以前全部位置($1$ 到 $i-1$ 的位置)上的數。spa
至少要有一個位置填上非 $0$ 的數。問最終有幾種填數方案,兩種填數方案不一樣當且僅當某個位置上填的數不一樣。code
要求即爲選出一些位置填數,而且造成嚴格遞增序列。get
發現把全部區間離散化,能夠組成 $\mathcal{O}(n)$ 個連續段,每一個連續段中的數的性質是相同的。it
由於 $n$ 不大(只有 $500$),考慮 DP。令 $\mathrm{f}[i][j]$ 表示前 $i$ 個位置,第 $i$ 個位置填了在段 $j$ 中的數的方案數。io
顯然若 $[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$。class
轉移的具體形式是:假設位置 $(k,i]$ 中一共有 $pos$ 個位置(包括 $i$ 自己)包含段 $j$,而段 $j$ 的長度爲 $len$,則 $\mathrm{f}[k][j']$ 貢獻 $\displaystyle\sum_{x=1}^{pos}\binom{pos-1}{x-1}\binom{len}{x}$ 倍給 $\mathrm{f}[i][j]$。這是由於咱們枚舉 $pos$ 個位置中填了 $x$ 個位置,可是第 $i$ 個位置必須填,因此乘上 $\displaystyle\binom{pos-1}{x-1}$,而後 $len$ 個可行數中選出 $x$ 個從小到大依次填入,因此再乘上 $\displaystyle\binom{len}{x}$。化簡一下係數:$\displaystyle\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$ 個數等價。sort
那麼咱們有 $\displaystyle\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$)。di
再利用前綴和優化,把第二維作前綴和,把轉移變成 $\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; }