题解 CF1559E Mocha and Stars

Beihang round & Chinese round 的题,本想着这场重新回到 Candidate Master,可是再一次验证了 Chinese Round 必掉分的理论。

如果做出 E 可就翻盘了啊 qwq

求出满足以下条件的整数数组 \(a\) 个数

  • 长度为 \(n\)
  • 和不超过 \(m\)
  • 最大公约数是 \(1\)
  • \(a_i \in [l_i, r_i]\)

如果没有 \(\gcd\) 的限制那么就是一个简单背包,容易想到用前缀和优化,时间复杂度就变成了 \(nm\)

主要的问题还是出在这个 \(\gcd\) 如何处理,容易想到要用容斥做,可是我却不知道应该怎样去容斥,在分解质因数的思路上花费了很多的时间,但是遗憾的是这是一条死路。
主要是没有想到一个容斥的小 trick

trick

在处理 \(\gcd\)\(x\) 的问题时,可以倒着枚举一个 \(i\),设 \(ans_i\)\(\gcd\) 恰好为 \(i\) 的方案数,\(f_i\)\(\gcd\)\(i\) 的倍数的方案数。

那么就可以用 \(ans_g = f_g - \sum_{g|i} ans_i\) 来求解。

用这个方法结合原来的背包,先用背包求出 \(f\),然后再进行下面的容斥就可以了。
具体地,设 \(f_{i, j}\) 是前 \(i\) 个和不超过 \(j\times g\) 的方案数,\(f_{i, j} = \sum f_{i-1, k}\)\(k\) 是枚举之前的和能到这里的,\(l\)\(r\) 都要除以 \(g\) 处理,然后前缀和优化即可。初始化是 \(f_{0, i} = 1\),因为是前缀和,每次叠了个 \(1\),实际上只有 \(f_{0, 0} = 1\) 目标状态是 \(f_{n, m/g}\)

但是赛场上我还是遇到了一个小问题改变了我思考的方向,在考虑容斥之前考虑求总数的时候觉得这个复杂度是 \(nm^2\) 的无法通过,所以放弃了这个方向的思考,转而去想着分解出来的质数出不出现的问题。
实际上这个复杂度根本就是 \(O(nm \ln m)\) 的,因为除以了 \(g\) 之后复杂度变成了调和级数。要对调和级数的复杂度有敏感度啊。

代码
#include <iostream>
#include <algorithm>
#define int long long
const int N = 55, M = 100005, P = 998244353;
int n, m, l[N], r[N], f[N][M], ans[M];
signed main() {
	std::cin >> n >> m;
	for (int i = 1; i <= n; i++) std::cin >> l[i] >> r[i];
	for (int g = m/n; g >= 1; g--) {
		for (int i = 0; i <= n; i++)	
			for (int j = 0; j <= m/g+1; j++) f[i][j] = 0;
		for (int j = 0; j <= m/g+1; j++) f[0][j] = 1;
		for (int i = 1; i <= n; i++) {
			int L = (l[i]+g-1)/g, R = r[i]/g;
			for (int a = L; a <= m/g+1; a++) {
				int cl = std::max(a-R, 0ll), cr = a-L;
				if (cl > cr) f[i][a] = 0;
				else f[i][a] = (f[i-1][cr] - (cl ? f[i-1][cl-1] : 0) + P) % P;
				if (a > 0) f[i][a] = (f[i][a] + f[i][a-1]) % P;
			}
		}
		ans[g] = f[n][m/g];
		for (int i = 2*g; i <= m; i += g) ans[g] = (ans[g] - ans[i] + P) % P;
	}
	std::cout << ans[1];
}
posted @ 2021-08-16 18:56  Acfboy  阅读(35)  评论(0编辑  收藏  举报