P3643 & CF1295F 题解
题意:
给定 个区间 ,表示 , 可以不选,要求选了的 单调递增,求方案数。
。
思路:
同类型题弱化版:CF1295F,每个必须选且不降。
非常有意思的技巧。
首先,总方案数是 。
所以我们只用求出所有单调不增的方案数即可。
最原始的思路:设 表示 时前 项单调不增的方案数。
转移也很好想:。
但是 的范围很大,没法算。
这时候我们就要改变一下状态。
注意到 ,我们发现可以把数轴分成不超过 个左闭右开的小区间,使得每一对 都可以用连续的几个这样的区间表示。
即我们存下所有的 然后离散化即可。
这样我们就把状态改成 表示 在第 个小区间的范围内,前 项单调不增的方案数。
空间复杂度 ,现在考虑转移。
首先枚举所有状态是 。
我们枚举上一个不在 区间的数的位置 ,以及它在那个区间,这个是 的。
现在我们相当于需要求有若干个数都在 区间内,它们单调不增的概率。
这个问题其实就是可重集,可以用挡板法解决。
设 区间长度为 ,则方案数为 。
由于 范围很小,所以我们直接暴力计算,复杂度 。
所以总的时间复杂度是 。
当然这题也可以继续优化到 ,但 也能过。
点击查看代码
#include <iostream>
#include <cstdio>
#include <map>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 55;
const int mod = 998244353;
int fpow(int a, int b, int p) {
if (b == 0)
return 1;
int ans = fpow(a, b / 2, p);
ans = (1ll * ans * ans) % p;
if (b % 2 == 1)
ans = (1ll * a * ans) % p;
return ans;
}
int n;
int l[MAXN] = {0}, r[MAXN] = {0};
int seg[MAXN * 2] = {0};
int dp[MAXN][MAXN * 2] = {{0}};
int inv[MAXN] = {0};
int cmb(int m, int n) {
int ans = m;
for (int i = 2; i <= n; i++)
ans = (1ll * ans * (m - i + 1) % mod * inv[i]) % mod;
return ans;
}
map<int, int> mp;
int main() {
cin >> n;
int sum = 1;
for (int i = 1; i <= n; i++)
inv[i] = fpow(i, mod - 2, mod);
for (int i = 1; i <= n; i++) {
cin >> l[i] >> r[i];
++r[i];
sum = (1ll * sum * (r[i] - l[i])) % mod;
mp[l[i]] = mp[r[i]] = 1;
}
sum = fpow(sum, mod - 2, mod);
int cur = 0;
for (auto &i: mp)
i.second = ++cur, seg[cur] = i.first;
for (int i = 1; i <= n; i++)
l[i] = mp[l[i]], r[i] = mp[r[i]];
for (int i = 1; i <= cur; i++)
dp[0][i] = 1;
for (int i = 1; i <= n; i++)
for (int j = l[i]; j < r[i]; j++)
for (int k = i - 1; k >= 0; k--) {
if (!(l[k + 1] <= j && j < r[k + 1]))
break;
if (k == 0)
dp[i][j] = (dp[i][j] + cmb(i + (seg[j + 1] - seg[j]) - 1, i) % mod) % mod;
else {
for (int l = j + 1; l <= cur; l++)
dp[i][j] = (dp[i][j] + 1ll * dp[k][l] * cmb(i - k + (seg[j + 1] - seg[j]) - 1, i - k) % mod) % mod;
}
}
int ans = 0;
for (int i = l[n]; i < r[n]; i++)
ans = (ans + dp[n][i]) % mod;
cout << (1ll * ans * sum) % mod << endl;
return 0;
}
说回原题,现在不同在于可以不选,我们需要一个引理:再 中选 个数,要求除了 之外都单调递增,方案数是 。
可以相当于从 个 和 这 个数中选 个一一映射,选第 个 表示第 个不选,剩下的对应 中的选法。
然后就可以用这个结论来算,前缀和优化,时间复杂度 。
点击查看代码
#include <iostream>
#include <cstdio>
#include <map>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 505;
const int mod = 1e9 + 7;
int n;
int a[N] = {0}, b[N] = {0};
int inv[N] = {0};
int C[N][N] = {{0}};
void init() {//先处理逆元
inv[1] = 1;
for (int i = 2; i <= n; i++)
inv[i] = 1ll * (mod - 1) * (mod / i) % mod * inv[mod % i] % mod;//, cout << i << " " << inv[i] << " " << 1ll * i * inv[i] % mod << endl;
C[0][0] = 1;
for (int i = 1; i <= n; i++) {
C[i][0] = 1;
for (int j = 1; j <= i; j++)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
}
}
map<int, int> mp;
int cur = 0;
int l[N * 2] = {0}, r[N * 2] = {0};
void initSeg() {//将区间离散化成小区间
for (auto &i: mp) {
i.second = ++cur;
r[cur - 1] = l[cur] = i.first;
}
// for (int i = 1; i <= cur; i++)
// cout << l[i] << " " << r[i] << endl;
for (int i = 1; i <= n; i++)
a[i] = mp[a[i]], b[i] = mp[b[i] + 1] - 1;//, cout << i << " " << a[i] << " " << b[i] << endl;
}
int dp[N][N * 2] = {{0}};
int f[N][N * 2] = {{0}};
int main() {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i] >> b[i], mp[a[i]] = mp[b[i] + 1] = 1;
init();
initSeg();
for (int j = 0; j <= cur; j++)
f[0][j] = 1;
for (int i = 1; i <= n; i++) {
for (int j = a[i]; j <= b[i]; j++) {
int cmb = r[j] - l[j];
int L = r[j] - l[j], m = 1;
for (int x = i - 1; x >= 0; x--) {
dp[i][j] = (dp[i][j] + 1ll * cmb * f[x][j - 1] % mod) % mod;
if (a[x] <= j && j <= b[x]) {
m++;
cmb = 1ll * cmb * (L + m - 1) % mod * inv[m] % mod;
}
}
}
for (int j = 1; j <= cur; j++)
f[i][j] = (f[i][j - 1] + dp[i][j]) % mod;
}
/* for (int i = 1; i <= n; i++)
for (int j = 1; j <= cur; j++)
cout << i << " " << j << " " << dp[i][j] << endl;*/
int ans = 0;
for (int i = 1; i <= n; i++)
ans = (ans + f[i][cur]) % mod;
cout << ans << endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】