• AtCoder Beginner Contest 268


    E - Chinese Restaurant (Three-Star Version)

    假设旋转 \(x\) 次时, \(n\) 个人失望值的总和为 \(c_x\),那么只要能求出 \(c_x, 0 \le x < n\) 就可以包含所有情况,然后再取最小值即可。

    对于第 \(0\) 个人,假设 \(p_0 = 0\),第 \(0\) 个人对 \(c_x\) 的贡献依次为 \(0, 1, 2, \dots, y - 1, y, y - 1, \dots, 1\);假设 \(p_0 = x\),那么贡献旋转左移 \(x\) 次即可得到对应的贡献。

    类似的,可以算出每个人对答案的贡献,但是朴素的算复杂度爆炸,需要优化。

    对于第 \(i\) 个人的贡献,可以视为区间加一个 \(1, 2, \dots, y - 1, y, y - 1, \dots, 1\) ,这种操作其实很经典,二阶差分后就只剩常数个非零值了,先只维护二阶差分数组就可以 \(O(1)\) 完成操作,操作完再恢复回来,就能得到原数组。

    AC代码
    // Problem: E - Chinese Restaurant (Three-Star Version)
    // Contest: AtCoder - UNIQUE VISION Programming Contest 2022 Summer (AtCoder Beginner Contest 268)
    // URL: https://atcoder.jp/contests/abc268/tasks/abc268_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() {}
    
    void SolveCase(int Case) {
      int n;
      std::cin >> n;
    
      std::vector<int> p(n);
      for (int i = 0; i < n; ++i) {
        std::cin >> p[i];
      }
    
      std::vector<i64> d2(2 * n + 1, 0);
    
      auto add = [&](int d) {
        int x = 0, y = n / 2, z = n / 2 + n % 2, w = n;
    
        logd(x, y, z, w);
    
        ++d2[x + d + 1];
        --d2[y + d + 1];
        --d2[z + d + 1];
        ++d2[w + d + 1];
    
        // std::vector<i64> d1(2 * n + 1);
        // for (int i = 1; i <= 2 * n; ++i)
        // d1[i] = d1[i - 1] + d2[i];
        // logd(d1);
        //
        // std::vector<i64> c(2 * n + 1);
        // for (int i = 1; i <= 2 * n; ++i)
        // c[i] = c[i - 1] + d1[i];
        // logd(c);
      };
    
      for (int i = 0; i < n; ++i) {
        int d = i <= p[i] ? p[i] - i : n + (p[i] - i);
        add(d);
      }
    
      std::vector<i64> d1(2 * n + 1);
      for (int i = 1; i <= 2 * n; ++i)
        d1[i] = d1[i - 1] + d2[i];
      logd(d1);
    
      std::vector<i64> c(2 * n + 1);
      for (int i = 1; i <= 2 * n; ++i)
        c[i] = c[i - 1] + d1[i];
      logd(c);
    
      for (int i = 1; i <= n; ++i)
        c[i] = c[i] + c[i + n];
      logd(c);
    
      std::cout << *std::min_element(c.begin() + 1, c.begin() + n + 1) << "\n";
    }
    
    

    F - Best Concatenation

    首先,将分数分成每个 \(s_i\) 内部的,以及不同 \(s_i\) 之间的。对于前者,不管怎么排列都不会影响,可以直接后缀和算。

    假设当前的排列为 \(T_1T_2\dots T_{|T|}\) ,假设 \(T_i\) 包含 \(cx_i\)X\(T_i\) 内的数位之和为 \(cd_i\) ,则此时的分数为

    \[score = \sum_{i = 1}^{n} cx_i \sum_{j = i + 1}^{n} cd_j \]

    交换 \(T_i\)\(T_{i+1}\) ,记 \(D = \sum_{j = i + 2}^{n}cd_j\) ,则分数的变化量为

    \[\Delta = cx_{i+1}(cd_i + D) + cx_i D - \left[ cx_i(cd_{i+1} + D) + cx_{i+1}D \right] \]

    如果交换后的方案更优,则 \(\Delta > 0\) ,即\(cx_{i+1}cd_i > cx_i cd_{i+1}\)

    那么根据这个条件去将 \(T_i\) 排序即可得到最优的方案。

    AC代码
    // Problem: F - Best Concatenation
    // Contest: AtCoder - UNIQUE VISION Programming Contest 2022 Summer (AtCoder Beginner Contest 268)
    // URL: https://atcoder.jp/contests/abc268/tasks/abc268_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() {}
    
    void SolveCase(int Case) {
      int n;
      std::cin >> n;
    
      i64 ans = 0;
      std::vector<std::pair<int, int>> c(n);
    
      for (int _ = 0; _ < n; ++_) {
        std::string s;
    
        std::cin >> s;
    
        int cx = 0, cd = 0;
        for (int i = s.size() - 1; i >= 0; --i) {
          if (s[i] == 'X') {
            ++cx;
            ans += cd;
          } else {
            cd += s[i] - '0';
          }
        }
        c[_] = {cx, cd};
      }
    
      std::sort(c.begin(), c.end(),
                [](const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) -> bool {
                  return i64(1) * lhs.first * rhs.second > i64(1) * rhs.first * lhs.second;
                });
    
      i64 s = 0;
      for (int i = n - 1; i >= 0; --i) {
        ans += s * c[i].first;
        s += c[i].second;
      }
    
      std::cout << ans << "\n";
    }
    
    

    G - Random Student ID

    对于字符串 \(s_i\) ,其排名等于小于等于它的字符串的数量之和。记 \(P_{i, j}\) 表示 \(s_j\) 小于等于 \(s_i\) 的概率,则 \(s_i\) 排名的期望等于 \(\sum_j P_{i, j}\)

    对于两个字符串 \(s_i\)\(s_j\) ,分类讨论:

    • 假设 \(s_j\)\(s_i\) 的前缀(包含 \(s_j = s_i\) )的情况,则 \(s_j\) 永远小于等于 \(s_i\),即 \(p_{i, j} = 1\)
    • 假设 \(s_i\)\(s_j\) 的前缀(不包含 \(s_j = s_i\) )的情况,则 \(s_j\) 永远大于 \(s_i\),即 \(p_{i, j} = 0\)
    • 剩余情况的话,就是 \(s_i\)\(s_j\)\(k - 1\) 个字符相同,第 \(k\) 个字符不同,那么个第 \(k\) 个字符的大小就决定了两个串的大小。然后根据对称性可得 \(p_{i, j} = \frac{1}{2}\)

    由此,假设有 \(a_i\)\(j\) 满足第一种情况, \(b_i\)\(j\) 满足第二种情况,则有 \(N - a_i - b_i\)\(j\) 满足第三种情况,\(\sum_j P_{i, j} = \frac{a_i - b_i + N}{2}\)

    然后 \(a_i\)\(b_i\) 可以通过 Trie 计算。

    然后完事了。

    AC代码
    // Problem: G - Random Student ID
    // Contest: AtCoder - UNIQUE VISION Programming Contest 2022 Summer (AtCoder Beginner Contest 268)
    // URL: https://atcoder.jp/contests/abc268/tasks/abc268_g
    // Memory Limit: 1024 MB
    // Time Limit: 3000 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 ValueType, ValueType mod_, typename SupperType>
    class Modular {
      static ValueType normalize(ValueType value) {
        if (value >= 0 && value < mod_)
          return value;
        value %= mod_;
        if (value < 0)
          value += mod_;
        return value;
      }
    
      static ValueType power(ValueType value, int64_t exponent) {
        ValueType result = 1;
        ValueType base = value;
        while (exponent) {
          if (exponent & 1)
            result = SupperType(result) * base % mod_;
          base = SupperType(base) * base % mod_;
          exponent >>= 1;
        }
        return result;
      }
    
     public:
      Modular(ValueType value = 0) : value_(normalize(value)) {}
    
      Modular(SupperType value) : value_(normalize(value % mod_)) {}
    
      ValueType value() const { return value_; }
    
      Modular inv() const { return Modular(power(value_, mod_ - 2)); }
    
      Modular power(int64_t exponent) const { return Modular(power(value_, exponent)); }
    
      friend Modular operator+(const Modular& lhs, const Modular& rhs) {
        ValueType result = lhs.value() + rhs.value() >= mod_ ? lhs.value() + rhs.value() - mod_
                                                             : lhs.value() + rhs.value();
        return Modular(result);
      }
    
      friend Modular operator-(const Modular& lhs, const Modular& rhs) {
        ValueType result = lhs.value() - rhs.value() < 0 ? lhs.value() - rhs.value() + mod_
                                                         : lhs.value() - rhs.value();
        return Modular(result);
      }
    
      friend Modular operator-(const Modular& lhs) {
        ValueType result = normalize(-lhs.value() + mod_);
        return result;
      }
    
      friend Modular operator*(const Modular& lhs, const Modular& rhs) {
        ValueType result = SupperType(1) * lhs.value() * rhs.value() % mod_;
        return Modular(result);
      }
    
      friend Modular operator/(const Modular& lhs, const Modular& rhs) {
        ValueType result = SupperType(1) * lhs.value() * rhs.inv().value() % mod_;
        return Modular(result);
      }
    
      std::string to_string() const { return std::to_string(value_); }
    
     private:
      ValueType value_;
    };
    
    // using Mint = Modular<int, 1'000'000'007, int64_t>;
    using Mint = Modular<int, 998'244'353, int64_t>;
    
    class Binom {
     private:
      std::vector<Mint> f, g;
    
     public:
      Binom(int n) {
        f.resize(n + 1);
        g.resize(n + 1);
    
        f[0] = Mint(1);
        for (int i = 1; i <= n; ++i)
          f[i] = f[i - 1] * Mint(i);
        g[n] = f[n].inv();
        for (int i = n - 1; i >= 0; --i)
          g[i] = g[i + 1] * Mint(i + 1);
      }
      Mint operator()(int n, int m) {
        if (n < 0 || m < 0 || m > n)
          return Mint(0);
        return f[n] * g[m] * g[n - m];
      }
    };
    
    struct Trie {
      Trie* child_[26];
    
      int id_;
      int e_;
      int up_, down_;
    
      Trie() {
        e_ = 0;
        for (int i = 0; i < 26; ++i)
          child_[i] = nullptr;
      }
    
      ~Trie() {
        for (int i = 0; i < 26; ++i) {
          if (child_[i]) {
            delete child_[i];
            child_[i] = nullptr;
          }
        }
      }
    
      void Insert(const std::string& s, int id) {
        int n = s.size();
        Trie* p = this;
    
        for (int i = 0; i < n; ++i) {
          int c = s[i] - 'a';
    
          if (p->child_[c] == nullptr) {
            p->child_[c] = new Trie();
          }
    
          p = p->child_[c];
        }
    
        ++p->e_;
        p->id_ = id;
      }
    
      void Work() {
        std::function<void(Trie*, int)> dfs = [&](Trie* p, int up) {
          p->up_ = up + p->e_;
          p->down_ = 0;
    
          for (int i = 0; i < 26; ++i) {
            if (p->child_[i]) {
              dfs(p->child_[i], p->up_);
              p->down_ += p->child_[i]->down_ + p->child_[i]->e_;
            }
          }
        };
        dfs(this, 0);
      }
    
      std::vector<Mint> Solve(int n) {
        std::vector<Mint> r(n);
    
        std::function<void(Trie*)> dfs = [&](Trie* p) {
          if (p->e_) {
            r[p->id_] = Mint(p->up_ - p->down_ + n) / Mint(2);
            logd(p->id_, p->up_, p->down_, Mint(p->up_ - p->down_ + n));
          }
    
          for (int i = 0; i < 26; ++i) {
            if (p->child_[i]) {
              dfs(p->child_[i]);
            }
          }
        };
        dfs(this);
    
        return r;
      }
    };
    
    void SolveCase(int Case) {
      int n;
      std::cin >> n;
    
      Trie trie;
      for (int i = 0; i < n; ++i) {
        std::string s;
        std::cin >> s;
    
        trie.Insert(s, i);
      }
    
      trie.Work();
      auto a = trie.Solve(n);
    
      for (auto x : a)
        std::cout << x.value() << "\n";
    }
    
    

    Ex - Taboo

    建出 AC 自动机,然后拿 \(s\) 在自动机上跑,如果跑到一个包含某个 \(T_i\)的状态,就把最后遍历到的\(s\)的字符改成 *,相当于重新从 AC 自动机的根开始跑。

    AC代码
    // Problem: Ex - Taboo
    // Contest: AtCoder - UNIQUE VISION Programming Contest 2022 Summer (AtCoder Beginner Contest 268)
    // URL: https://atcoder.jp/contests/abc268/tasks/abc268_h
    // Memory Limit: 1024 MB
    // Time Limit: 4000 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 <int alpha = 256, typename String = std::string>
    struct AcAutomaton {
     public:
      struct Node {
       public:
        Node()
            : child_(alpha, nullptr),
              trans_(alpha, nullptr),
              fail_(nullptr),
              pass_(0),
              end_(0),
              mark_(0) {}
    
        ~Node() {
          for (int i = 0; i < alpha; ++i) {
            if (child_[i]) {
              delete child_[i];
              child_[i] = nullptr;
            }
          }
        }
    
       public:
        std::vector<Node*> child_;
        std::vector<Node*> trans_;
        Node* fail_;
        int pass_;
        int end_;
    
       public:
        int mark_;
      };
    
     public:
      void InsertTrie(const String& s) {
        Node* p = root_;
        for (int i = 0; i < s.size(); ++i) {
          int c = s[i];
          if (p->child_[c] == nullptr) {
            p->child_[c] = new Node();
          }
          p = p->child_[c];
          ++p->pass_;
        }
        ++p->end_;
      }
    
      void BuildFail() {
        std::queue<Node*> q;
        q.push(root_);
    
        while (!q.empty()) {
          Node* p = q.front();
          q.pop();
    
          for (int c = 0; c < alpha; ++c) {
            if (p->child_[c]) {
              p->trans_[c] = p->child_[c];
    
              if (p == root_)
                p->trans_[c]->fail_ = root_;
              else
                p->trans_[c]->fail_ = p->fail_->trans_[c];
    
              q.push(p->child_[c]);
            } else {
              if (p == root_)
                p->trans_[c] = root_;
              else
                p->trans_[c] = p->fail_->trans_[c];
            }
          }
        }
      }
    
      void Work() {
        std::queue<Node*> q;
        q.push(root_);
    
        while (!q.empty()) {
          Node* p = q.front();
          q.pop();
    
          if (p != root_)
            p->mark_ = (p->end_ > 0) | p->fail_->mark_;
    
          for (int c = 0; c < alpha; ++c) {
            if (p->child_[c]) {
              q.push(p->child_[c]);
            }
          }
        }
      }
    
     public:
      AcAutomaton() : root_(new Node()) {}
    
      ~AcAutomaton() { delete root_; }
    
     public:
      Node* root_;
    };
    using AcAM = AcAutomaton<26, std::vector<int>>;
    
    void SolveCase(int Case) {
      std::string s;
      std::cin >> s;
    
      AcAM ac;
      int n;
      std::cin >> n;
      std::string t;
      for (int i = 0; i < n; ++i) {
        std::cin >> t;
    
        std::vector<int> v(t.size());
        for (int i = 0; i < t.size(); ++i)
          v[i] = t[i] - 'a';
        ac.InsertTrie(v);
      }
      ac.BuildFail();
      ac.Work();
    
      AcAM::Node* p = ac.root_;
      int ans = 0;
      for (int i = 0; i < s.size(); ++i) {
        int c = s[i] - 'a';
        p = p->trans_[c];
        if (p->mark_) {
          p = ac.root_;
          ++ans;
        }
      }
    
      std::cout << ans << "\n";
    }
    
    
  • 相关阅读:
    大数据时代下的隐私保护(二)
    互联网+究竟想干什么
    MySql触发器使用解说
    系统函数C字符串的实现(12):strset
    參加湛江新教育群线下活动有感
    Axure多人协作
    hdu (1556 树状数组)
    [leetcode]Subsets II
    Oracle insert all语句介绍
    JQuery 实现锚点链接之间的平滑滚动
  • 原文地址:https://www.cnblogs.com/zengzk/p/16704577.html
Copyright © 2020-2023  润新知