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

題目傳送門: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;
}
相關文章
相關標籤/搜索