• 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。

  • 相关阅读:
    分析SIX锁和锁分区导致的死锁
    导数中的最小化日志记录:测试和分析
    导数中的最小化日志记录:背景和理论
    Redis学习笔记(十一) 服务器
    Redis学习笔记(十) 客户端
    Redis学习笔记(九) AOF持久化
    Redis学习笔记(八) RDB持久化
    Redis学习笔记(七) 数据库
    Redis学习笔记(六) 对象
    Redis学习笔记(五) 压缩列表
  • 原文地址:https://www.cnblogs.com/zengzk/p/16770464.html
Copyright © 2020-2023  润新知