题意#
数列 在下列条件下随机生成。
是给定的区间 内的正整数,等概率随机。
求 单调不增的概率,对 取模。
,.
分析#
首先容易想到,概率可以由总方案数和单调不增的方案数求得,其中总方案数为:
只需求出单调不增的序列个数 ,则:
对于值域较小的情况,可以想到设 表示:选好了前 个数且 的情况下的方案数。
转移只需:
但可惜本题中值域较大,考虑做优化。
想到离散化。
对区间端点进行离散化,用 表示所有端点坐标中数值第 大的数,
表示左端点离散化后编号, 表示右端点离散化后编号。
此时需要微调DP状态,但思路类似。
设 表示选好了前 个数且 ,所形成单调不增数列的方案数。
转移时,由于进行了离散化,我们无法通过枚举确定 与 的大小关系。
退而求其次,再枚举 ,保证 的值都在 内,计算这个长度为 的区间内的方案数。
记可能的取值个数 .
需要在其中选 个数单调不增,方案数为:
这个组合数直接递推预处理就好了:
转移时,倒序枚举所有合法的 ,具体地:
每个 转移完之后,需要做前缀和,
这样对下一次转移才是对的。
最终 ,答案为:
代码#
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 60, mod = 998244353;
inline int power(int x, int k) {
int ret = 1;
while(k) {
if(k & 1) ret = ret * x % mod;
x = x * x % mod;
k >>= 1;
}
return ret;
}
int n, l[MAXN], r[MAXN], p[MAXN << 1], N;
int f[MAXN][MAXN << 1], C[MAXN];
signed main() {
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> l[i] >> r[i];
p[++N] = l[i], p[++N] = ++r[i];
}
sort(p + 1, p + N + 1);
N = unique(p + 1, p + N + 1) - p - 1;
for(int i = 1; i <= n; i++) {
l[i] = lower_bound(p + 1, p + N + 1, l[i]) - p;
r[i] = lower_bound(p + 1, p + N + 1, r[i]) - p;
}
l[0] = 1, r[0] = N + 1;
for(int i = 1; i <= N; i++) f[0][i] = 1;
for(int i = 1; i <= n; i++) {
for(int j = l[i]; j < r[i]; j++) {
int len = p[j + 1] - p[j];
C[1] = len;
for(int k = 2; k <= i; k++)
C[k] = C[k - 1] * (len + k - 1) % mod * power(k, mod - 2) % mod;
for(int k = i - 1; k >= 0; k--) {
(f[i][j] += f[k][j + 1] * C[i - k] % mod) %= mod;
if(j < l[k] || j >= r[k]) break;
}
}
for(int j = N - 1; j > 0; j--) (f[i][j] += f[i][j + 1]) %= mod;
}
int ans = f[n][1];
for(int i = 1; i <= n; i++)
ans = ans * power(p[r[i]] - p[l[i]], mod - 2) % mod;
cout << ans << endl;
return 0;
}
cpp