时间限制 \(2\,\text{sec}\)
空间限制 \(256\,\text{MB}\)
题目来源 CodeForces。
题目大意
给定 \(n\) 个区间 \([l_i,\, r_i]\) 和一个常数 \(m\) ,在每个区间内选出一个正整数 \(a_i\) 构成一个数列 ,满足
- \(\displaystyle\sum^{n}_{i=1}a_i \leq m\) ,
- \(\gcd \{a_i \} = 1\) 。
求不同的数列 \(a\) 的取法。答案对 \(998244353\) 取模。
数据范围
区间数量 \(2\leq n \leq 50\)
常数 \(1 \leq m \leq 10^5\)
区间 \(1 \leq l_i \leq r_i \leq m\)
分析
本题的限制特别多,我们先从 \(\gcd\) 入手——因为可以使用 Möbius 反演直接删去这个条件。
记 \(f(x)\) 为满足限制 1 的条件下 \(\gcd\) 为 \(x\) 的选法数,\(g(x)\) 为满足限制 1 的条件下 \(\gcd\) 为 \(x\) 的倍数的选法数,本题的答案 \(f(1)\) 可以反演得到
现在我们来求每一个 \(g(d)\) 的值。 固定 \(d\),我们要想 \(\gcd\) 为 \(d\) 的倍数,经典的结论是取
此时求和限制也相应地变成了
现在我们可以任取上述区间里的数,就变成了一个背包问题:记 \(\text{dp}_{i,\, j}\) 为选取到第 \(i\) 个区间为止,求和为 \(j\) 的方案数,记除以 \(d\) 后的新区间为 \([l_i^{'},\, r_i^{'}]\),转移为
上面的转移是 \(O\left(n \cdot \left(\dfrac{m}{d} \right)^2 \right)\) 的,但是我们维护 \(\text{dp}\) 的前缀和就可以优化为 \(O\left(n\cdot \dfrac{m}{d}\right)\) 。最后
注意到,我们线性枚举 \(d\) 时内层的 dp 复杂度是调和级数的,因此总复杂度可以接受。
时间复杂度 \(O(nm\log m)\)
题解
using im = ModBaseInt<MOD>;void solve(const int &T)
{euler_sieve(1'000'00);int n, m;std::cin >> n >> m;std::vector<pii> a(n + 1);for (int i = 1; i <= n; i++)std::cin >> a[i].first >> a[i].second;im ans = 0;std::vector<im> g(m + 1, 0);std::vector<std::vector<im>> dp(n + 1, std::vector<im>(m + 1, 0));std::vector<std::vector<im>> pre(n + 1, std::vector<im>(m + 1, 0));for (int d = 1; d <= m; d++){dp[0][0] = pre[0][0] = 1;for (int j = 1; j <= m / d; j++)pre[0][j] = 1;for (int i = 1; i <= n; i++){int l = (a[i].first + d - 1) / d, r = a[i].second / d; // 新区间for (int j = l; j <= m / d; j++){if (j - r - 1 >= 0) // 注意边界dp[i][j] = pre[i - 1][j - l] - pre[i - 1][j - r - 1];elsedp[i][j] = pre[i - 1][j - l];pre[i][j] = pre[i][j - 1] + dp[i][j];}}ans += im(mu[d]) * pre[n][m / d];}std::cout << ans;
}