CF1295F - Good Contest(dp,区间离散化,组合数学)
题目
\(a_i\)在范围\([l_i, r_i]\)等概率取值,求序列\(\{a_i\}\)非递增的概率。\(n\le 50\),\(l_i\le r_i \le 998244351\)
题解
即求序列非递增的方案数。
由于区间范围非常大,而区间数又很少。因此可以离散化端点,给区间分段。这样分下来总段数就是\(O(n)\)级别了。可以使用dp解决。
这样每个大区间,每分成若干个连续的右闭左开的小区间,以及最后一个左闭右闭仅仅包含一个数的小区间。
用\(dp[i][j]\)代表前\(i\)个区间,当前在第\(j\)个小区间的方案数。转移很好想,关键在于求解一个区间内\(k\)个非递增位置的方案数。这个用差分的方法,转化为差值大于等于0,差值求和小于等于区间长度,可以用隔板法解决。
注意最后左闭右闭小区间的处理即可。
点击展开代码
#include <bits/stdc++.h>
#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N)
typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f
const int N = 1e2 + 10;
const int MX = 1e5 + 10;
const int M = 998244353;
const double eps = 1e-5;
ll dp[N][N];
ll rfact[MX];
typedef pair<int, int> PII;
PII segs[N];
vector<int> num;
inline ll qpow(ll a, ll b, ll m) {
ll res = 1;
while(b) {
if(b & 1) res = (res * a) % m;
a = (a * a) % m;
b = b >> 1;
}
return res;
}
int main() {
IOS;
rfact[0] = 1;
for(int i = 1; i < MX; i++) {
rfact[i] = rfact[i - 1] * qpow(i, M - 2, M) % M;
}
num.push_back(0);
int n;
cin >> n;
ll tot = 1;
for(int i = 1; i <= n; i++) {
int l, r;
cin >> l >> r;
tot = (r - l + 1) * tot % M;
segs[i] = {l, r};
num.push_back(l);
num.push_back(r);
}
sort(num.begin(), num.end());
num.erase(unique(num.begin(), num.end()), num.end());
for(int i = 1; i <= n; i++) {
segs[i].first = lower_bound(num.begin(), num.end(), segs[i].first) - num.begin();
segs[i].second = lower_bound(num.begin(), num.end(), segs[i].second) - num.begin();
}
for(int i = 0; i < N; i++) dp[0][i] = 1;
for(int i = 1; i <= n; i++) {
int l = segs[i].first, r = segs[i].second;
for(int j = r; j >= l; j--) {
int rg;
if(j - 1 >= l) rg = num[j] - num[j - 1];
else rg = 1;
ll pw = rg;
for(int k = i - 1; k >= 0; k--) {
if(k && segs[k].first == j)
dp[i][j] = (dp[i][j] + dp[k][j] % M * pw % M * rfact[i - k] % M) % M;
else
dp[i][j] = (dp[i][j] + dp[k][j + 1] % M * pw % M * rfact[i - k] % M) % M;
pw = pw * (rg + i - k) % M;
if(!(segs[k].second >= j && segs[k].first < j)) break;
}
dp[i][j] = (dp[i][j] + dp[i][j + 1]) % M;
}
for(int j = l - 1; j >= 0; j--) {
dp[i][j] = dp[i][j + 1];
}
}
cout << dp[n][0] * qpow(tot, M - 2, M) % M << endl;
}
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步