P3643 [APIO2016] 划艇
题意
给定数列 \(a, b\),试求出序列 \(S\) 的方案数,使得:
- \(a_i \le S_i \le b_i, S_{i - 1} < S_i\) 或 \(S_i = 0\)。
\(S\) 不能是全 \(0\) 序列。
\(a_i, b_i \le 10 ^ 9, n \le 500\)
Sol
不难想到一个 trivial 的思路。
设 \(f_{i, j}\) 表示确定了 \(S_1 \to S_i\),并且 \(S_i = j\) 的方案数。
\[f_{0, 0} = 1
\]
\[f_{i, j} = \left \{ \begin{array}{**lr**} \sum_{i' = 0} ^ {i - 1} \sum_{j' = 0} ^ {j - 1} f_{i', j'} , & a_i \le j \le b_i \\ 0 , & otherwise\end{array} \right.
\]
答案就是 \(\sum_{i}\sum_{j} f_{i,j}\)
考虑将第二维离散化掉。
\(f_{i, j}\) 表示确定了 \(S_1 \to S_i\),且 \(S_i\) 落在第 \(j\) 段的方案数。
如果两个端点都有贡献显然会算重,先先将题目中的 \([a_i, b_i]\) 转为 \([a_i, b_i)\)。
相同地,考虑去枚举上一个不在第 \(j\) 段的 \(S_{i'}\)。
设 \(m\) 表示第 \(i'\) 到 \(i\),\(a, b\) 完全包含第 \(j\) 段的个数,\(L\) 表示第 \(j\) 段的长度。
不难发现,现在的问题是如何求出在 \(L\) 中选 \(m\) 个数,使得非零数严格递增。
集中注意力,发现这个东西就是 \(\binom{L + m - 1}{m}\),上面 \(-1\) 是因为第 \(i\) 个数不能为 \(0\)。
\[f_{0, 0} = 1
\]
\[f_{i, j} = \left \{ \begin{array}{**lr**} \sum_{i' = 0} ^ {i - 1} \sum_{j' = 0} ^ {j - 1} \binom{L + m - 1}{m} f_{i', j'} , & a_i \le j \le b_i \\ 0 , & otherwise\end{array} \right.
\]
考虑上面的式子。
发现 \(\binom{L + m - 1}{m}\) 与 \(j\) 无关,提出来。
\[\sum_{i' = 0} ^ {i - 1} \binom{L + m - 1}{m} \sum_{j' = 0} ^ {j - 1} f_{i', j'}
\]
简单前缀和优化即可。
设 \(g_{i, j} = \sum_{j' = 0} ^ {j} f_{i, j'}\)。
\[\sum_{i' = 0} ^ {i - 1} \binom{L + m - 1}{m} g_{i', j' - 1}
\]
组合数每次向杨辉三角的右下递推,这是平凡的。
时间复杂度: \(O(n ^ 3)\)
Code
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <array>
#include <vector>
#define int long long
#define pii pair <int, int>
using namespace std;
#ifdef ONLINE_JUDGE
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 23], *p1 = buf, *p2 = buf, ubuf[1 << 23], *u = ubuf;
#endif
int read() {
int p = 0, flg = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') flg = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
p = p * 10 + c - '0';
c = getchar();
}
return p * flg;
}
void write(int x) {
if (x < 0) {
x = -x;
putchar('-');
}
if (x > 9) {
write(x / 10);
}
putchar(x % 10 + '0');
}
#define fi first
#define se second
const int N = 1005, mod = 1e9 + 7;
array <array <int, N>, N> f, g;
array <pii, N> isl;
void Mod(int &x) {
if (x >= mod) x -= mod;
if (x < 0) x += mod;
}
namespace Mth {
array <int, N> fac, inv;
int pow_(int x, int k, int p) {
int ans = 1;
while (k) {
if (k & 1) ans = ans * x % p;
x = x * x % p;
k >>= 1;
}
return ans;
}
void init(int n = 1003) {
inv[0] = 1;
for (int i = 1; i <= n; i++)
inv[i] = pow_(i, mod - 2, mod);
/*
inv[n] = pow_(fac[n], mod - 2, mod);
for (int i = n; i; i--)
inv[i - 1] = inv[i] * i % mod;
*/
}
}
signed main() {
int n = read();
vector <int> tp;
Mth::init();
for (int i = 1; i <= n; i++) {
isl[i].fi = read(), isl[i].se = read() + 1;
tp.push_back(isl[i].fi), tp.push_back(isl[i].se);
}
sort(tp.begin(), tp.end());
tp.erase(unique(tp.begin(), tp.end()), tp.end());
for (int i = 1; i <= n; i++) {
isl[i].fi = lower_bound(tp.begin(), tp.end(), isl[i].fi) - tp.begin() + 1;
isl[i].se = lower_bound(tp.begin(), tp.end(), isl[i].se) - tp.begin() + 1;
/* write(isl[i].fi), putchar(32); */
/* write(isl[i].se), puts(""); */
}
for (int i = 0; i < tp.size(); i++)
g[0][i] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j < tp.size(); j++) {
int l = tp[j] - tp[j - 1], m = 1, tp0 = l;
if (isl[i].fi > j || j >= isl[i].se) continue;
for (int _i = i - 1; ~_i; _i--) {
/* write(tp0), puts("^"); */
/* } */
f[i][j] += tp0 * g[_i][j - 1] % mod, Mod(f[i][j]);
if (_i && isl[_i].fi <= j && j < isl[_i].se)
tp0 = tp0 * (l + m) % mod * Mth::inv[m + 1] % mod, m++;
}
}
for (int j = 1; j < tp.size(); j++)
g[i][j] = g[i][j - 1] + f[i][j], Mod(g[i][j]);
}
/* for (int i = 1; i <= n; i++) { */
/* for (int j = 1; j < tp.size(); j++) */
/* write(f[i][j]), putchar(32); */
/* puts(""); */
/* } */
int ans = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j < tp.size(); j++)
ans += f[i][j], Mod(ans);
write(ans), puts("");
return 0;
}