CF-CodeTon-3解题报告

比赛传送门

A. Indirect Sort

题意:有一个排列 \(a\),每次可以选三个不同的位置,从左到右依次为 \(i,j,k\)。如果 \(a_i>a_k\),将 \(a_i\) 加上 \(a_j\),否则交换 \(a_j,a_k\)。问是否能将其排成非降序列。

贪心。如果第一个元素是 \(1\),则一定可以:每次用 \(1\) 来当 \(i\),即可任意交换 \(j,k\)。如果不是,则一定不可以:因为对于 \(1\) 所在的位置,要么将它移到左边,要么将它增加,要么让前面的减少。容易证明这三种都是不可行的。

By tourist

#include <bits/stdc++.h>

using namespace std;

#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  int tt;
  cin >> tt;
  while (tt--) {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
      cin >> a[i];
    }
    bool ok = true;
    for (int i = 0; i < n; i++) {
      ok &= (a[0] <= a[i]);
    }
    cout << (ok ? "Yes" : "No") << '\n';
  }
  return 0;
}

B. Maximum Substring

题意:定义一个 01 字符串的权值为:如果只有 0 或只有 1,则为 0/1 个数的平方,否则为 0 的个数乘 1 的个数。给定一个字符串,找出所有子串的最大权值。

分类讨论。有 0 有 1 时,全选一定最优,否则为最长的连续 0/1 子串长度的平方。三种情况取最大值即可。

By tourist

#include <bits/stdc++.h>

using namespace std;

#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  int tt;
  cin >> tt;
  while (tt--) {
    int n;
    cin >> n;
    string s;
    cin >> s;
    int mx = 1;
    int t = 1;
    for (int i = 1; i < n; i++) {
      if (s[i] == s[i - 1]) {
        t += 1;
        mx = max(mx, t);
      } else {
        t = 1;
      }
    }
    int x = 0;
    for (int i = 0; i < n; i++) {
      x += (s[i] == '0');
    }
    int y = n - x;
    long long ans = (long long) x * y;
    ans = max(ans, (long long) mx * mx);
    cout << ans << '\n';
  }
  return 0;
}

C. Complementary XOR

题意:有两个 01 串 \(a,b\),每次操作可以选一个区间,将 \(a\) 这段区间内的元素取反, \(b\) 这段元素外的区间取反。构造一种方案,在 \(n+5\) 次操作内将两个串归零,或判断无解。

容易发现,进行操作后 \(a,b\) 的异或值将会完全取反,所以只有当 \(a=b\)\(a=inv(b)\) 时才可能有解。如果 \(a=inv(b)\),我们可以先选整个串,将 \(a\) 取反,转化为 \(a=b\) 的情况。此后,当奇数次操作后 \(a=inv(b)\),偶数次操作后 \(a=b\)。问题转化为:每次可以选 \(a\) 的一段区间取反,恰好偶数次操作内将 \(a\) 归零。

于是,我们先对每个 1 的位单独选中取反,最多 \(O(n)\) 次。如果此时用了奇数次操作,我们考虑如何再“浪费”奇数次操作将其变成偶数。一种方法是将第一位取反,再将剩下的取反,最后将全部取反。使用三次操作,结果不变。

By jiangly

#include <bits/stdc++.h>

using i64 = long long;

void solve() {
    int n;
    std::cin >> n;

    std::string a, b;
    std::cin >> a >> b;

    for (int i = 0; i < n; i++) {
        if (a[i] ^ b[i] ^ a[0] ^ b[0]) {
            std::cout << "NO\n";
            return;
        }
    }

    std::cout << "YES\n";

    std::vector<std::array<int, 2>> ans;
    for (int i = 0; i < n; i++) {
        if (a[i] == '1') {
            ans.push_back({i, i + 1});
        }
    }
    if (a[0] ^ b[0] ^ (ans.size() & 1)) {
        ans.push_back({0, 1});
        ans.push_back({1, n});
        ans.push_back({0, n});
    }

    std::cout << ans.size() << "\n";
    for (auto [l, r] : ans) {
        std::cout << l + 1 << " " << r << "\n";
    }
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t;
    std::cin >> t;

    while (t--) {
        solve();
    }

    return 0;
}

D. Count GCD

题意:给你一个数组 \(a\),求数组 \(b\) 的方案数,满足 \(b\) 的值域在 \([1,m]\),且 \(a\)\(b\) 的前缀 \(\gcd\)

由于 \(b\) 的每一位独立,问题转化为求 \(b_i\) 的方案数,满足 \(\gcd(a_{i-1},b_i)=a_i\)。式子两边同时除以 \(a_i\),则要求 \(\gcd(\frac{a_{i-1}}{a_i},\frac{b_i}{a_i})=1\) 的方案数。我们可以枚举每个 \(a_{i-1}\) 的质因数,然后容斥。由于 \(10^9\) 内的数不同的质因数个数不超过 \(10\) 个,\(2^10\) 枚举每一种方案即可。

By tourist

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  int tt;
  cin >> tt;
  while (tt--) {
    int n, m;
    cin >> n >> m;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
      cin >> a[i];
    }
    Mint ans = 1;
    for (int i = 0; i < n - 1; i++) {
      int x = a[i];
      int y = a[i + 1];
      if (x % y != 0) {
        ans = 0;
        break;
      }
      if (x == y) {
        ans *= m / x;
        continue;
      }
      int bound = m / y;
      int num = x / y;
      vector<int> fs;
      for (int j = 2; j * j <= num; j++) {
        if (num % j == 0) {
          fs.push_back(j);
          while (num % j == 0) {
            num /= j;
          }
        }
      }
      if (num > 1) {
        fs.push_back(num);
      }
      int sz = (int) fs.size();
      int cnt = 0;
      for (int mask = 0; mask < (1 << sz); mask++) {
        int sign = 1;
        int u = 1;
        for (int j = 0; j < sz; j++) {
          if (mask & (1 << j)) {
            sign *= -1;
            u *= fs[j];
          }
        }
        cnt += sign * (bound / u);
      }
      ans *= cnt;
    }
    cout << ans << '\n';
  }
  return 0;
}
posted @ 2023-03-01 16:06  曹轩鸣  阅读(25)  评论(0编辑  收藏  举报