(mathcal{Description})
给定 (n) 个区间 ([a_i,b_i))(注意原题是闭区间,这里只为方便后文描述),求 ({c_n}) 的个数,使得:
- (forall i~~~~c_i=0lor c_iin[a_i,b_i))。
- (forall i<j~~~~c_i ot=0land c_j ot=0Rightarrow c_i<c_j)。
对 (10^9+7) 取模。
(nle500),(1le a_ile b_ile10^9)。
(mathcal{Solution})
一个很 naive 的 DP 想法,(f(i,j)) 表示考虑前 (i) 个位置,(c_i=j~(j ot=0)) 时的方案数。问题在于第二维开销过大,考虑离散化所有端点坐标。
先来一个引理,取值在 ([a,b)),长度为 (n) 的上升整数序列的个数为 (inom{b-a}{n}),显然选 (n) 个数就可以了。
再来一个引理,取值在 ([a,b)cup{0}),长度为 (n),非零的位置是上升整数序列的序列个数为 (inom{b-a+n}{n}),证明也很显然,有几个 (0) 可以选,虽然不同的 (0) 可以任意排列,但看上去都是一样的。所以钦定 (0) 的大小关系后就等价于令区间为 ([a-n,b]),长度为 (n) 时的上一引理。
接着刚才的思路,离散化时,排过序的端点们把坐标轴分为若左闭右开的区间,从左开始第 (t) 个区间称作第 (t) 段。令 (f(i,j)) 表示考虑前 (i) 个位置,(c_i) 属于前 (j) 段时的方案数。设 ([j,j+1)) 实际映射 ([a,b)),枚举 (k<i),转移:
[f(i,j)leftarrow f(i,j)+f(k,j-1)inom{b-a+x-1}{x},~~~~ ext{where }x=1+sum_{t=k+1}^{j-1}[jin[a_t,b_t)]
]
组合数运用了引理二。注意钦定 (c_i) 不为 (0),所以上面 (-1)。
扫 (f) 就结束了,交换枚举顺序,第二维还可以滚掉。复杂度 (mathcal O(n^3))。
(mathcal{Code})
/* Clearink */
#include <cstdio>
#include <algorithm>
const int MAXN = 500, MOD = 1e9 + 7;
int n, a[MAXN + 5], b[MAXN + 5], tmp[MAXN * 2 + 5], inv[MAXN + 5];
int f[MAXN + 5], comb[MAXN + 5];
inline int mul ( long long a, const int b ) { return a * b % MOD; }
inline int& addeq ( int& a, const int b ) { return ( a += b ) < MOD ? a : a -= MOD; }
int main () {
scanf ( "%d", &n ), inv[1] = 1;
for ( int i = 1; i <= n; ++ i ) {
if ( i > 1 ) inv[i] = mul ( MOD - MOD / i, inv[MOD % i] );
scanf ( "%d %d", &a[i], &b[i] ), ++ b[i];
tmp[2 * i - 1] = a[i], tmp[i << 1] = b[i];
}
std::sort ( tmp + 1, tmp + ( n << 1 | 1 ) );
int m = std::unique ( tmp + 1, tmp + ( n << 1 | 1 ) ) - tmp - 1;
for ( int i = 1; i <= n; ++ i ) {
a[i] = std::lower_bound ( tmp + 1, tmp + m + 1, a[i] ) - tmp;
b[i] = std::lower_bound ( tmp + 1, tmp + m + 1, b[i] ) - tmp;
}
f[0] = 1;
for ( int j = 1, len; j < m; ++ j ) {
len = tmp[j + 1] - tmp[j], comb[0] = 1;
for ( int i = 1; i <= n; ++ i ) {
comb[i] = mul ( mul ( comb[i - 1], len + i - 1 ), inv[i] );
}
for ( int i = n; i; -- i ) {
if ( j < a[i] || b[i] <= j ) continue;
for ( int k = i - 1, c = 1; ~k; -- k ) {
addeq ( f[i], mul ( comb[c], f[k] ) );
if ( a[k] <= j && j < b[k] ) ++ c;
}
}
}
int ans = 0;
for ( int i = 1; i <= n; ++ i ) addeq ( ans, f[i] );
printf ( "%d
", ans );
return 0;
}
(mathcal{Details})
目前洛谷最优解,兔的代码吸口氧真的快到飞起 www。