题解 P3643 【[APIO2016]划艇】

xht

2020-01-30 21:41:19

Solution

首先将区间离散化成若干个左闭右开的区间。 设 $f_{i, j}$ 表示考虑到 $a_i$,$a_i$ 在第 $j$ 个区间内的方案数。 枚举上一个在 $j$ 之前的位置 $a_k$ 转移。 设 $k + 1 \sim i$ 中有 $c$ 个位置可以选择第 $j$ 个区间,设第 $j$ 个区间的长度为 $l_j$,则方案数为 $\binom{l_j + c}{c}$。 需要前缀和优化,时间复杂度 $\mathcal O(n^3)$。 ```cpp const int N = 507; int n, a[N], b[N], c[N*2], t; modint f[N], g[N], ans; int main() { rd(n); for (int i = 1; i <= n; i++) rd(a[i]), rd(b[i]), c[++t] = a[i], c[++t] = ++b[i]; sort(c + 1, c + t + 1), t = unique(c + 1, c + t + 1) - (c + 1); for (int i = 1; i <= n; i++) a[i] = lower_bound(c + 1, c + t + 1, a[i]) - c, b[i] = lower_bound(c + 1, c + t + 1, b[i]) - c; f[0] = 1; for (int j = 1; j < t; j++) { int l = c[j+1] - c[j]; g[0] = 1; for (int i = 1; i <= n; i++) g[i] = g[i-1] * (l + i - 1) / i; for (int i = n; i; i--) if (a[i] <= j && j < b[i]) for (int c = 1, k = i - 1; ~k; k--) { f[i] += g[c] * f[k]; if (a[k] <= j && j < b[k]) ++c; } } for (int i = 1; i <= n; i++) ans += f[i]; print(ans); return 0; } ```