题目大意:
a i a_i ai在 [ l i , r i ] [l_i,r_i] [li,ri]等概率选择一个整数,询问 { a n } \{a_n\} {an}非递增的概率
解题思路(思路来源:crashed):
首先能够得到一个非常暴力的
d
p
dp
dp方程:
d
p
[
i
]
[
j
]
=
∑
k
=
m
a
x
(
j
,
l
i
−
1
)
r
i
−
1
d
p
[
i
−
1
]
[
k
]
dp[i][j]=\sum_{k=max(j,l_{i-1})}^{r_{i-1}}dp[i-1][k]
dp[i][j]=∑k=max(j,li−1)ri−1dp[i−1][k],
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]代表第
i
i
i个数在区间范围内选择
j
j
j的前
i
i
i个数的合理方案数
很显而易见,这个方程再怎么优化都会超时,所以不是这样子。
接下来先看该题目的一个子问题:一个大小为 m m m的区间内选择 n n n个数非递增的方案数:
- 大小为 m m m的区间可以看成 m m m个不同的桶,现在问题就转换成, n n n个一样的球放进 m m m个不同桶内的方案数,根据插空法,方案数为: C n + m − 1 m − 1 o r C n + m − 1 n C_{n+m-1}^{m-1} \ or \ C_{n+m-1}^{n} Cn+m−1m−1 or Cn+m−1n
接下来是原问题的解析:
- 对于这类区间很大的问题,可以先试着将其进行离散化以及把闭区间转换为开区间
- 原区间: [ L i , R i ) [L_i,R_i) [Li,Ri),离散化后为: [ l i . r i ) [l_i.r_i) [li.ri),且因为离散化后 [ l i , l i + 1 ) , [ l i + 1 , l i + 2 ) . . . [ r i − 1 , r i ) [l_i,l_i+1),[l_i+1,l_i+2)...[r_i-1,r_i) [li,li+1),[li+1,li+2)...[ri−1,ri)都分别对应原来的一个大区间
- 设 d p [ i ] [ j ] dp[i][j] dp[i][j]为前 i i i个数且第 i i i个数在离散化后的 [ j , j + 1 ) [j,j+1) [j,j+1)(对应未离散化的一个大区间, j ∈ [ l i , r i ) j\in[l_i,r_i) j∈[li,ri))区间内的方案数
- 所以可以枚举连续有多少个数(从 i i i往前)同时出现 [ j , j + 1 ) [j,j+1) [j,j+1)来推导 d p dp dp方程: d p [ i ] [ j ] = ∑ k = 1 i [ ∀ : k ≤ p ≤ i , [ j , j + 1 ) ∈ [ l k , r k ) ] ∑ t ( t > j ) d p [ k − 1 ] [ t ] ⋅ C L + i − k i − k + 1 dp[i][j]=\sum_{k=1}^{i}[\forall: \ k\le p \le i, [j,j+1) \in[l_k,r_k)]\sum_{t(t>j)}dp[k-1][t]\cdot C_{L+i-k}^{i-k+1} dp[i][j]=∑k=1i[∀: k≤p≤i,[j,j+1)∈[lk,rk)]∑t(t>j)dp[k−1][t]⋅CL+i−ki−k+1, L L L为 [ j , j + 1 ) [j,j+1) [j,j+1)对应的原来区间长度
- 因为 L L L可能很大,所以组合数要 O ( n ) O(n) O(n)算,后半部分的 ∑ t ( t > j ) d p [ k − 1 ] [ t ] \sum_{t(t>j)}dp[k-1][t] ∑t(t>j)dp[k−1][t]可以通过后缀和优化,所以总复杂度是 O ( n 4 ) O(n^4) O(n4)
AC代码:
#include <bits/stdc++.h>
#define ft first
#define sd second
#define pb push_back
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) //不能跟puts混用
#define seteps(N) fixed << setprecision(N)
#define endl "\n"
const int maxn = 55;
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
const ll mod = 998244353;
ll qpow(ll a, ll b) {
ll res = 1;
while (b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll C(ll n, ll m) {
if (n < m) return 0;
ll res = 1;
for (int i = 1; i <= m; i++) res = res * (n - i + 1) % mod * qpow(i, mod - 2) % mod;
return res;
}
int n, l[maxn], r[maxn];
int lef[maxn], rig[maxn];
int p[maxn << 1], cntp;
ll dp[maxn][maxn << 1];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> l[i] >> r[i], r[i] += 1, p[++cntp] = l[i], p[++cntp] = r[i];
sort (p + 1, p + cntp + 1);
cntp = unique(p + 1, p + cntp + 1) - (p + 1);
for (int i = 1; i <= n; i++) lef[i] = lower_bound(p + 1, p + cntp + 1, l[i]) - p, rig[i] = lower_bound(p + 1, p + cntp + 1, r[i]) - p;
for (int i = 1; i <= cntp; i++) dp[0][i] = 1;
lef[0] = 1, rig[0] = cntp + 1;
for (int i = 1; i <= n; i++) {
for (int j = lef[i]; j < rig[i]; j++) {
int L = p[j + 1] - p[j];
for (int k = i; k; k--) {
if (!(lef[k] <= j && j + 1 <= rig[k])) break;
dp[i][j] = (dp[i][j] + dp[k - 1][j + 1] * C(L + i - k, i - k + 1) % mod) % mod;
}
}
for (int j = cntp - 1; j; j--)
dp[i][j] = (dp[i][j + 1] + dp[i][j]) % mod;
}
ll ans = dp[n][1];
for (int i = 1; i <= n; i++)
ans = ans * qpow(r[i] - l[i], mod - 2) % mod;
cout << ans << endl;
return 0;
}