AtCoder Beginner Contest 272

E - Add and Mex

每个 \(a_i\) 只有在 \(0 \le a_i + k i \le n\) 时才会有贡献,即对于 \(i\) 只有 \(O(\frac{n}{i})\) 个操作是有效的。所以需要考虑的只有 \(O(n\log n)\)\(a_i + ki\)

借助小顶堆从小到大枚举 \(a_i + ki\) 即可。

AC代码
// Problem: E - Add and Mex
// Contest: AtCoder - AtCoder Beginner Contest 272
// URL: https://atcoder.jp/contests/abc272/tasks/abc272_e
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#define CPPIO \
  std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);

#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#define ASSERT(x) ;
#define serialize() std::string("")
#endif

using i64 = int64_t;
using u64 = uint64_t;

void Initialize();
void SolveCase(int Case);

int main(int argc, char* argv[]) {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    SolveCase(t);
  }
  return 0;
}

void Initialize() {}

template <typename T>
using min_heap = std::priority_queue<T, std::vector<T>, std::greater<T>>;

std::mt19937 rng(time(0));
int rnd(int l, int r) {
  return l + rng() % (r - l + 1);
}

void SolveCase(int Case) {
  int n, m;
  std::cin >> n >> m;
  // n = m = 2e5;

  min_heap<std::array<i64, 3>> q;
  for (int i = 1; i <= n; ++i) {
    int x;
    std::cin >> x;
    // x = rnd(-1e9, 1e9);

    i64 d = std::max(1, (-x + i - 1) / i);
    if (d <= m)
      q.push({x + d * i, i, d});
  }

  std::vector<i64> ans(m + 1, 0);
  while (!q.empty()) {
    auto [value, index, count] = q.top();
    q.pop();

    // logd(value, index, count);

    if (ans[count] == value) {
      ++ans[count];
    }

    if (count + 1 <= m && value + index <= n)
      q.push({value + index, index, count + 1});
  }

  for (int i = 1; i <= m; ++i)
    std::cout << ans[i] << "\n";
}

F - Two Strings

\(a = s + s + t + t\) 的后缀数组,得到 \(rk\)\(lcp\) 数组。

由于 \(f(s, i)\)\(f(t, j)\) 的长度都还是 \(n\),所以这里算出来的 \(rk\) 是不准的,所以还需要根据 \(lcp\) 修正一下,具体就是后缀排序完之后,如果相邻的两个串 \(lcp \ge n\) 就认为这两个串是相等的。

然后根据对应第一个 \(s\) 和第一个 \(t\)\(rk\) 就可以得出所有 \(f(s, i)\)\(f(t, j)\) 的排名,根据这个排名就能计算出答案。

AC代码
// Problem: F - Two Strings
// Contest: AtCoder - AtCoder Beginner Contest 272
// URL: https://atcoder.jp/contests/abc272/tasks/abc272_f
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#define CPPIO \
  std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);

#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#define ASSERT(x) ;
#define serialize() std::string("")
#endif

using i64 = int64_t;
using u64 = uint64_t;

void Initialize();
void SolveCase(int Case);

int main(int argc, char* argv[]) {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    SolveCase(t);
  }
  return 0;
}

void Initialize() {}

/**
 * Build Suffix Array in O(n \log n) with binary lifting and radix sort.
 *
 * Idea
 *
 *   Assume that the rank of all substrings with lenght w is known, let call it
 * rank_{w}, then let rank_w(i) be the first key, and rank_w(i + w) be the
 * second key. rank_{2w} can be calculated by using radix sort.
 *
 * Reference
 *
 *   https://oi-wiki.org/string/sa/
 */
template <int alpha = 26, typename String>
std::tuple<std::vector<int>, std::vector<int>, std::vector<int>> SuffixArray(
    const String& s) {
  int n = s.size(), m = alpha;

  /**
   * suffix(i) means s_i...s_{n-1}
   * sa[i] is start position fo the i-th smallest prefix
   * rank[i] is rank of the suffix(i)
   * lcp[i] means longest common prefix between suffix(sa[i]) and suffix(sa[i -
   * 1])
   */
  std::vector<int> sa(n), rank(n), lcp(n);
  std::vector<int> count(std::max(m, n), 0), old_rank(n), temp(n);

  // build sa with binary lifting and raidx sort.
  for (int i = 0; i < n; ++i) {
    rank[i] = s[i];
    ++count[rank[i]];
  }
  for (int i = 1; i < m; ++i) {
    count[i] += count[i - 1];
  }
  for (int i = n - 1; i >= 0; --i) {
    sa[--count[rank[i]]] = i;
  }

  for (int length = 1; length < n; length = length * 2) {
    // second key.
    int p = 0;
    for (int i = n - length; i < n; ++i)
      temp[p++] = i;
    for (int i = 0; i < n; ++i)
      if (sa[i] >= length)
        temp[p++] = sa[i] - length;

    // first key.
    std::fill(count.begin(), count.end(), 0);
    for (int i = 0; i < n; ++i)
      ++count[rank[temp[i]]];
    for (int i = 1; i < m; ++i)
      count[i] += count[i - 1];
    for (int i = n - 1; i >= 0; --i) {
      sa[--count[rank[temp[i]]]] = temp[i];
    }

    old_rank = rank;
    m = 0;
    rank[sa[0]] = m++;
    for (int i = 1; i < n; ++i) {
      if (old_rank[sa[i]] == old_rank[sa[i - 1]] &&
          ((sa[i] + length < n ? old_rank[sa[i] + length] : -1) ==
           (sa[i - 1] + length < n ? old_rank[sa[i - 1] + length] : -1))) {
        rank[sa[i]] = m - 1;
      } else {
        rank[sa[i]] = m++;
      }
    }

    if (m == n)
      break;
  }

  // longest common prefix
  for (int i = 0, k = 0; i < n; ++i) {
    if (rank[i] == 0)
      continue;
    if (k)
      --k;
    while (s[i + k] == s[sa[rank[i] - 1] + k])
      ++k;
    lcp[rank[i]] = k;
  }

  return {sa, rank, lcp};
}

void SolveCase(int Case) {
  int n;
  std::cin >> n;

  std::string s, t;
  std::cin >> s;
  std::cin >> t;

  std::string a = s + s + t + t;
  auto [sa, rk, lcp] = SuffixArray<256>(a);
  for (int i = 1; i < 4 * n; ++i) {
    if (lcp[i] >= n) {
      rk[sa[i]] = rk[sa[i - 1]];
    } else {
      rk[sa[i]] = rk[sa[i - 1]] + 1;
    }
  }

  std::vector<std::pair<int, int>> p;
  p.reserve(2 * n);
  for (int i = 0; i < n; ++i) {
    p.push_back({rk[i], 0});
  }
  for (int i = 2 * n; i < 3 * n; ++i) {
    p.push_back({rk[i], 1});
  }
  std::sort(p.begin(), p.end());

  i64 ans = 0, count = 0;
  for (auto [rank, type] : p) {
    if (type == 0)
      ++count;
    else
      ans += count;
  }
  std::cout << ans << "\n";
}

G - Yet Another mod M

假设存在某个可行解 \(M\)\(b_i = a_i \mod M\)\(b_i\) 的主元素为 \(z\) ,满足 \(a_i \mod M = z\)\(a_i\) 构成的集合为 \(S\)

假设任选两个 \(a_x\)\(a_y\),则 \(a_x\)\(a_y\) 同属于 \(S\) 的概率存在下界 \(\frac{1}{4}\),所以期望选择 \(4\) 次就能选中满足 \(a_x\)\(a_y\) 同属于 \(S\)\((x, y)\)

然后假设某次随机到了 \(a_x\)\(a_y\) ,考虑根据 \(a_x\)\(a_y\) 求出 \(M\)。易得:

\[a_x = k_1M + z \\ a_y = k_2M + z \]

两式相减可得 \(M \mid (a_x - a_y)\)

由此,随机选择 \(a_x\)\(a_y\), 每次枚举 \(|a_x - a_y|\) 的因子看是不是满足条件的 \(M\)

写了个单次的复杂度为 \(O(\sqrt{V} n\log n)\) 的算法,然后至多尝试 \(10\) 次就过题了。

AC代码
// Problem: G - Yet Another mod M
// Contest: AtCoder - AtCoder Beginner Contest 272
// URL: https://atcoder.jp/contests/abc272/tasks/abc272_g
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#define CPPIO \
  std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);

#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#define ASSERT(x) ;
#define serialize() std::string("")
#endif

using i64 = int64_t;
using u64 = uint64_t;

void Initialize();
void SolveCase(int Case);

int main(int argc, char* argv[]) {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    SolveCase(t);
  }
  return 0;
}

void Initialize() {}

std::mt19937 rng(115514);
int rnd(int l, int r) {
  return l + rng() % (r - l + 1);
}

void SolveCase(int Case) {
  int n;
  std::cin >> n;

  std::vector<int> a(n);
  for (int i = 0; i < n; ++i)
    std::cin >> a[i];

  std::vector<int> b(n);
  auto check = [&](int m) {
    for (int i = 0; i < n; ++i)
      b[i] = a[i] % m;
    std::sort(b.begin(), b.end());
    int mx = 0;
    for (int i = 0; i < n; ++i) {
      int j = i;
      while (j + 1 < n && b[j + 1] == b[i])
        ++j;
      mx = std::max(mx, j - i + 1);
      i = j;
    }
    return 2 * mx > n;
  };
  auto work = [&](int x, int y) {
    int z = std::abs(a[x] - a[y]);
    for (int m = 1; m * m <= z; ++m) {
      if (z % m == 0) {
        if (m >= 3 && check(m))
          return m;

        if (m * m != z && z / m >= 3 && check(z / m))
          return z / m;
      }
    }
    return -1;
  };

  for (int i = 0; i < 10; ++i) {
    int x = rnd(0, n - 1);
    int y;
    do {
      y = rnd(0, n - 1);
    } while (x == y);

    int ans = work(x, y);
    if (ans != -1) {
      std::cout << ans << "\n";
      return;
    }
  }
  std::cout << "-1\n";
}

Ex - Flipping Coins 2

这题基本没有人过,有点吓人。

To be solved。

posted @ 2022-10-08 22:11  _Backl1ght  阅读(178)  评论(1编辑  收藏  举报