比赛链接:Here

1559A. Mocha and Math

题意:

给定一个区间,选择区间内的值执行 & 操作使得区间最大值最小化

观察样例发现:令 x = (1 << 30) - 1Codeforces Round #738 (Div. 2) - 图1 答案

证明:

我们假设答案是 x。 在它的二进制表示中,只有在所有 Codeforces Round #738 (Div. 2) - 图2 的二进制表示中该位为 Codeforces Round #738 (Div. 2) - 图3 时,该位才会为 Codeforces Round #738 (Div. 2) - 图4 。否则,我们可以使用一个操作使 x 中的该位变为 Codeforces Round #738 (Div. 2) - 图5 ,这是一个较小的答案。
所以我们可以初始设置 Codeforces Round #738 (Div. 2) - 图6 或者 Codeforces Round #738 (Div. 2) - 图7 。 然后我们对序列进行迭代,使 Codeforces Round #738 (Div. 2) - 图8 ,最终 x 是 anwser。

  1. int main() {
  2. cin.tie(nullptr)->sync_with_stdio(false);
  3. int n;
  4. cin >> n;
  5. int x = (1 << 30) - 1;
  6. for (int i = 0, a; i < n; ++i) {
  7. cin >> a;
  8. x &= a;
  9. }
  10. cout << x << "\n";
  11. }

1559B.Mocha and Red and Blue

根据贪心思想,找到第一个非 ? 的下标,然后根据下标位置的值去枚举情况即可

  1. int main() {
  2. cin.tie(nullptr)->sync_with_stdio(false);
  3. int _; for (cin >> _; _--;) {
  4. int n; string s;
  5. cin >> n >> s;
  6. for (int i = 0; i < n; ++i)
  7. if (s[i] == '?' and i and s[i - 1] != '?')
  8. s[i] = 'R' ^ 'B' ^ s[i - 1];
  9. if (s.back() == '?') s.back() = 'R';
  10. for (int i = n - 2; i >= 0; --i)
  11. if (s[i] == '?') s[i] = 'R' ^ 'B' ^ s[i + 1];
  12. cout << s << "\n";
  13. }
  14. }

1559C.Mocha and Hiking

路线规律题,

如果 Codeforces Round #738 (Div. 2) - 图9 那么路径肯定有 Codeforces Round #738 (Div. 2) - 图10%5Cto%201%5Cto2%5Cto…%5Cto%20n%5D#card=math&code=%5B%28n%20%2B%201%29%5Cto%201%5Cto2%5Cto…%5Cto%20n%5D&id=FPytq)

如果 Codeforces Round #738 (Div. 2) - 图11 那么路径为 Codeforces Round #738 (Div. 2) - 图12%5D#card=math&code=%5B1%5Cto2%5Cto…%5Cto%20n%5Cto%20%28n%20%2B%201%29%5D&id=Fdw4N)

对于其他情况来说:由于 Codeforces Round #738 (Div. 2) - 图13 ,所以肯定存在整数 Codeforces Round #738 (Div. 2) - 图14 使得 Codeforces Round #738 (Div. 2) - 图15 ,那么路径为 Codeforces Round #738 (Div. 2) - 图16%5Cto%20(i%20%2B%201)%5Cto%20(i%20%2B%202)%5Cto…%5Cto%20n%5D#card=math&code=%5B1%5Cto%202%5Cto…%5Cto%20i%5Cto%28n%20%2B%201%29%5Cto%20%28i%20%2B%201%29%5Cto%20%28i%20%2B%202%29%5Cto…%5Cto%20n%5D&id=Vwyk1)

具体证明可以参考哈密顿路径

  1. const int N = 1e4 + 10;
  2. int a[N];
  3. int main() {
  4. cin.tie(nullptr)->sync_with_stdio(false);
  5. int _; for (cin >> _; _--;) {
  6. int n; cin >> n;
  7. for (int i = 1; i <= n; ++i) cin >> a[i];
  8. int idx = n;
  9. for (int i = 1; i < n; ++i)
  10. if (a[i] == 0 and a[i + 1] == 1) {
  11. idx = i; break;
  12. }
  13. if (a[1] == 1) {
  14. cout << n + 1 << " ";
  15. for (int i = 1; i <= n; ++i) cout << i << " \n"[i == n];
  16. continue;
  17. }
  18. for (int i = 1; i <= idx; ++i)
  19. cout << i << " ";
  20. cout << n + 1 << " ";
  21. for (int i = idx + 1; i <= n; ++i) cout << i << " ";
  22. cout << "\n";
  23. }
  24. }

1559D1.Mocha and Diana (Easy Version)

D1是一个暴力枚举 + 并查集的裸题,D2就懵逼了,不知道怎么维护

  1. const int N = 2e3 + 10;
  2. int f1[N], f2[N];
  3. int find1(int x) {return f1[x] == x ? x : f1[x] = find1(f1[x]);}
  4. int find2(int x) {return f2[x] == x ? x : f2[x] = find2(f2[x]);}
  5. void merge1(int x, int y) { f1[find1(x)] = find1(y);}
  6. void merge2(int x, int y) { f2[find2(x)] = find2(y);}
  7. int main() {
  8. cin.tie(nullptr)->sync_with_stdio(false);
  9. int n, m1, m2;
  10. cin >> n >> m1 >> m2;
  11. for (int i = 1; i <= n; ++i) f1[i] = f2[i] = i;
  12. for (int i = 1, u, v; i <= m1; ++i) {
  13. cin >> u >> v;
  14. merge1(u, v);
  15. }
  16. for (int i = 1, u, v; i <= m2; ++i) {
  17. cin >> u >> v;
  18. merge2(u, v);
  19. }
  20. cout << min(n - m1 - 1, n - m2 - 1) << "\n";
  21. for (int i = 1; i <= n; ++i)
  22. for (int j = 1; j <= n; ++j) {
  23. if (j == i) continue;
  24. if (find1(i) != find1(j) && find2(i) != find2(j)) {
  25. f1[find1(i)] = find1(j);
  26. f2[find2(i)] = find2(j);
  27. cout << i << " " << j << '\n';
  28. }
  29. }
  30. }

1559D2. Mocha and Diana (Hard Version)

  1. struct DSU {
  2. vector<int> f, siz;
  3. DSU(int n) : f(n), siz(n, 1) {iota(f.begin(), f.end(), 0);}
  4. int find(int x) {return x == f[x] ? x : f[x] = find(f[x]);}
  5. bool same(int x, int y) {return find(x) == find(y);}
  6. bool merge(int x, int y) {
  7. x = find(x), y = find(y);
  8. if (x == y) return false;
  9. siz[x] += siz[y];
  10. f[y] = x;
  11. return true;
  12. }
  13. int size(int x) {return siz[find(x)];}
  14. };
  15. int main() {
  16. cin.tie(nullptr)->sync_with_stdio(false);
  17. int n, m1, m2;
  18. cin >> n >> m1 >> m2;
  19. DSU f1(n), f2(n);
  20. for (int i = 0, u, v; i < m1; ++i) {
  21. cin >> u >> v;
  22. --u, --v;
  23. f1.merge(u, v);
  24. }
  25. for (int i = 0, u, v; i < m2; ++i) {
  26. cin >> u >> v;
  27. --u, --v;
  28. f2.merge(u, v);
  29. }
  30. int ans = n - 1 - max(m1, m2);
  31. cout << ans << "\n";
  32. vector<int> v1[n], v2[n];
  33. while (ans > 0) {
  34. for (int i = 0; i < n; ++i) v1[i].clear(), v2[i].clear();
  35. for (int i = 0; i < n; ++i) {
  36. v1[f1.find(i)].push_back(i);
  37. v2[f2.find(i)].push_back(i);
  38. }
  39. int i = 0, j = 0;
  40. while (1) {
  41. while (i < n && f1.find(i) != i) i += 1;
  42. while (j < n && f2.find(j) != j) j += 1;
  43. if (i == n || j == n) break;
  44. int a = -1, b = -1, c = -1, d = 0;
  45. for (auto x : v1[i]) {
  46. if (!f2.same(x, j)) a = x;
  47. else c = x;
  48. }
  49. for (auto x : v2[j]) {
  50. if (!f1.same(x, i)) b = x;
  51. else c = x;
  52. }
  53. if (a != -1 && b != -1) {
  54. cout << a + 1 << " " << b + 1 << "\n";
  55. f1.merge(a, b);
  56. f2.merge(b, a);
  57. } else {
  58. while (f1.same(d, i) || f2.same(d, j)) d += 1;
  59. cout << c + 1 << " " << d + 1 << "\n";
  60. f1.merge(c, d);
  61. f2.merge(c, d);
  62. }
  63. ans -= 1;
  64. i += 1, j += 1;
  65. }
  66. }
  67. }

1559E. Mocha and Stars

E题在赛时没想到正解,现在学习一下官方的题解

说实话,似乎E题在多校见过?

首先让我们忽略 Codeforces Round #738 (Div. 2) - 图17 的限制,令 Codeforces Round #738 (Div. 2) - 图18#card=math&code=f%28%5Bl_1%2Cl_2%2C…%2Cl_n%5D%2C%5Br_1%2Cr_2%2C…%2Cr_n%5D%2CM%29&id=pGMUF) 为整数 Codeforces Round #738 (Div. 2) - 图19#card=math&code=%28a_1%2Ca_2%2C..%2Ca_n%29&id=OAeBO) 的个数满足以下两个条件

  • Codeforces Round #738 (Div. 2) - 图20
  • 对于所有的 iCodeforces Round #738 (Div. 2) - 图21 都在 Codeforces Round #738 (Div. 2) - 图22 范围之间

所以我们可以通过前缀和优化背包DP来达到在 Codeforces Round #738 (Div. 2) - 图23#card=math&code=%5Cmathcal%7BO%7D%28nM%29&id=GyKCo) 内完成计算

此时再来考虑 Codeforces Round #738 (Div. 2) - 图24 的约束条件,设 Codeforces Round #738 (Div. 2) - 图25#card=math&code=%CE%BC%28n%29&id=XgTG3) 为莫比乌斯函数,Codeforces Round #738 (Div. 2) - 图26#card=math&code=g%28a_1%2Ca_2%2C%E2%80%A6%2Ca_n%29&id=QOp5A)为 Codeforces Round #738 (Div. 2) - 图27 ,如果Codeforces Round #738 (Div. 2) - 图28#card=math&code=%28a_1%2Ca_2%2C%E2%8B%AF%2Ca_n%29&id=TY8n6) 满足我们提到的两个条件(没有 Codeforces Round #738 (Div. 2) - 图29 的约束),否则为 Codeforces Round #738 (Div. 2) - 图30

我们想要的答案是:

Codeforces Round #738 (Div. 2) - 图31%3D1%5Dg(a1%2Ca_2%2C…%2Ca_n)%20%5C%5C%0A%3D%20%5Csum%5Climits%7Ba1%3Dl_1%7D%5E%7Br_1%7D%5Csum%5Climits%7Ba2%3Dl_2%7D%5E%7Br_2%7D…%5Csum%5Climits%7Ban%3Dl_n%7D%5E%7Br_n%7Dg(a_1%2Ca_2%2C…%2Ca_n)%5Csum%5Climits%7Bd%7C%5Cgcd(a1%2Ca_2%2C…%2Ca_n)%7D%CE%BC(d)%5C%5C%0A%3D%5Csum%5Climits%7Ba1%3Dl_1%7D%5E%7Br_1%7D%5Csum%5Climits%7Ba2%3Dl_2%7D%5E%7Br_2%7D…%5Csum%5Climits%7Ban%3Dl_n%7D%5E%7Br_n%7Dg(a_1%2Ca_2%2C…%2Ca_n)%5Csum%5Climits%7Bd%7Ca1%2Cd%7Ca_2%2C..%2Cd%7Ca_n%7D%CE%BC(d)%5C%5C%0A%3D%5Csum%5Climits%7Bd%3D1%7D%5EM%CE%BC(d)%5Csum%5Climits%7Ba_1%3D%E2%8C%88%5Cfrac%7Bl_1%7Dd%E2%8C%89%7D%5E%7B%E2%8C%8A%5Cfrac%7Br_1%7Dd%E2%8C%8B%7D…%5Csum%5Climits%7Ban%3D%E2%8C%88%5Cfrac%7Bl_n%7Dd%E2%8C%89%7D%5E%7B%E2%8C%8A%5Cfrac%7Br_n%7Dd%E2%8C%8B%7D%20g(a_1d%2Ca_2d%2C…%2Ca_nd)%0A%5Cend%7Barray%7D%0A#card=math&code=%5Cbegin%7Barray%7D%7Bll%7D%0A%5Csum%5Climits%7Ba1%3Dl_1%7D%5E%7Br_1%7D%5Csum%5Climits%7Ba2%3Dl_2%7D%5E%7Br_2%7D…%5Csum%5Climits%7Ban%3Dl_n%7D%5E%7Br_n%7D%5B%5Cgcd%28a_1%2Ca_2%2C…%2Ca_n%29%3D1%5Dg%28a_1%2Ca_2%2C…%2Ca_n%29%20%5C%5C%0A%3D%20%5Csum%5Climits%7Ba1%3Dl_1%7D%5E%7Br_1%7D%5Csum%5Climits%7Ba2%3Dl_2%7D%5E%7Br_2%7D…%5Csum%5Climits%7Ban%3Dl_n%7D%5E%7Br_n%7Dg%28a_1%2Ca_2%2C…%2Ca_n%29%5Csum%5Climits%7Bd%7C%5Cgcd%28a1%2Ca_2%2C…%2Ca_n%29%7D%CE%BC%28d%29%5C%5C%0A%3D%5Csum%5Climits%7Ba1%3Dl_1%7D%5E%7Br_1%7D%5Csum%5Climits%7Ba2%3Dl_2%7D%5E%7Br_2%7D…%5Csum%5Climits%7Ban%3Dl_n%7D%5E%7Br_n%7Dg%28a_1%2Ca_2%2C…%2Ca_n%29%5Csum%5Climits%7Bd%7Ca1%2Cd%7Ca_2%2C..%2Cd%7Ca_n%7D%CE%BC%28d%29%5C%5C%0A%3D%5Csum%5Climits%7Bd%3D1%7D%5EM%CE%BC%28d%29%5Csum%5Climits%7Ba_1%3D%E2%8C%88%5Cfrac%7Bl_1%7Dd%E2%8C%89%7D%5E%7B%E2%8C%8A%5Cfrac%7Br_1%7Dd%E2%8C%8B%7D…%5Csum%5Climits%7Ba_n%3D%E2%8C%88%5Cfrac%7Bl_n%7Dd%E2%8C%89%7D%5E%7B%E2%8C%8A%5Cfrac%7Br_n%7Dd%E2%8C%8B%7D%20g%28a_1d%2Ca_2d%2C…%2Ca_nd%29%0A%5Cend%7Barray%7D%0A&id=uIQ3M)

因为 Codeforces Round #738 (Div. 2) - 图32 可以被改写成 Codeforces Round #738 (Div. 2) - 图33 ,等价于

Codeforces Round #738 (Div. 2) - 图34f(%E2%8C%88%5Cfrac%20%7Bl1%7Dd%E2%8C%89%2C…%2C%E2%8C%88%5Cfrac%20%7Bl_n%7Dd%E2%8C%89%2C%E2%8C%8A%5Cfrac%20%7Br_1%7Dd%E2%8C%8B%2C…%2C%E2%8C%8A%5Cfrac%20%7Br_n%7Dd%E2%8C%8B%2C%E2%8C%8A%5Cfrac%20Md%E2%8C%8B)%0A#card=math&code=%5Csum%5Climits%7Bd%3D1%7D%5EM%CE%BC%28d%29f%28%E2%8C%88%5Cfrac%20%7Bl_1%7Dd%E2%8C%89%2C…%2C%E2%8C%88%5Cfrac%20%7Bl_n%7Dd%E2%8C%89%2C%E2%8C%8A%5Cfrac%20%7Br_1%7Dd%E2%8C%8B%2C…%2C%E2%8C%8A%5Cfrac%20%7Br_n%7Dd%E2%8C%8B%2C%E2%8C%8A%5Cfrac%20Md%E2%8C%8B%29%0A&id=kkQgB)

时间复杂度:Codeforces Round #738 (Div. 2) - 图35%20%3D%20%5Cmathcal%7BO%7D(nM%5C%20log%5C%20M)#card=math&code=%5Cmathcal%7BO%7D%28n%5Csum%5Climits_%7Bi%3D1%7D%5EM%E2%8C%8A%5Cfrac%20Mi%E2%8C%8B%29%20%3D%20%5Cmathcal%7BO%7D%28nM%5C%20log%5C%20M%29&id=jTcTd)

  1. #define maxn 100086
  2. const int p = 998244353;
  3. int n, m;
  4. int l[maxn], r[maxn];
  5. int f[maxn], sum[maxn];
  6. int cal(int d) {
  7. int M = m / d;
  8. f[0] = 1;
  9. for (int i = 1; i <= M; i++) f[i] = 0;
  10. for (int i = 1; i <= n; i++) {
  11. int L = (l[i] + d - 1) / d, R = r[i] / d;
  12. if (L > R) return 0;
  13. for (int j = 0; j <= M; j++) sum[j] = (f[j] + (j ? sum[j - 1] : 0)) % p;
  14. for (int j = 0; j <= M; j++) {
  15. f[j] = ((j - L >= 0 ? sum[j - L] : 0) + p - (j - R - 1 >= 0 ? sum[j - R - 1] : 0)) % p;
  16. }
  17. }
  18. int ans = 0;
  19. for (int i = 1; i <= M; i++) ans = (ans + f[i]) % p;
  20. return ans;
  21. }
  22. int prm[maxn], cnt, mu[maxn];
  23. bool tag[maxn];
  24. int main() {
  25. cin >> n >> m;
  26. for (int i = 1; i <= n; ++i) cin >> l[i] >> r[i];
  27. mu[1] = 1;
  28. for (int i = 2; i <= m; i++) {
  29. if (!tag[i]) prm[++cnt] = i, mu[i] = p - 1;
  30. for (int j = 1; j <= cnt && prm[j] * i <= m; j++) {
  31. tag[i * prm[j]] = true;
  32. if (i % prm[j]) mu[i * prm[j]] = (p - mu[i]) % p;
  33. else break;
  34. }
  35. }
  36. int ans = 0;
  37. for (int i = 1; i <= m; i++) ans = (ans + 1ll * mu[i] * cal(i)) % p;
  38. cout << ans;
  39. }

最后在看H神的代码时发现另外的一个代码思路

  1. const int mod = 998244353;
  2. void add(int &u, int v) {
  3. u += v;
  4. if (u >= mod) u -= mod;
  5. }
  6. void sub(int &u, int v) {
  7. u -= v;
  8. if (u < 0) u += mod;
  9. }
  10. int main() {
  11. cin.tie(nullptr)->sync_with_stdio(false);
  12. int n, m; cin >> n >> m;
  13. vector<int> l(n), r(n);
  14. vector<int> np(m + 1), ans(m + 1), p;
  15. for (int i = 0; i < n; i += 1) cin >> l[i] >> r[i];
  16. for (int i = 2; i <= m; i += 1) {
  17. if (not np[i]) {
  18. p.push_back(i);
  19. for (int j = i * 2; j <= m; j += i) np[j] = 1;
  20. }
  21. }
  22. for (int i = 1; i <= m; i += 1) {
  23. vector<int> L(n), R(n);
  24. int M = m / i, ok = 1;
  25. for (int j = 0; j < n; j += 1) {
  26. L[j] = (l[j] + i - 1) / i;
  27. R[j] = r[j] / i;
  28. if (L[j] > R[j]) ok = 0;
  29. M -= L[j];
  30. R[j] -= L[j];
  31. }
  32. if (not ok or M < 0) continue;
  33. vector<int> dp(M + 1);
  34. dp[0] = 1;
  35. for (int j = 0; j < n; j += 1) {
  36. for (int i = 1; i <= M; i += 1) add(dp[i], dp[i - 1]);
  37. for (int i = M; i >= 0; i -= 1)
  38. if (i > R[j]) sub(dp[i], dp[i - R[j] - 1]);
  39. }
  40. for (int x : dp) add(ans[i], x);
  41. }
  42. for (int x : p)
  43. for (int i = 1; i * x <= m; i += 1)
  44. sub(ans[i], ans[i * x]);
  45. cout << ans[1];
  46. }