• 2022 Google Kick Start Round C


    New Password

    题意

    给一个字符串,问最少几步能补到满足下列条件:

    • 长度至少为7
    • 至少有一个小写字母
    • 至少有一个大写字母
    • 至少有一个数字
    • 至少有一个特殊符号
    思路

    先补充到满足后面4个条件,大概就是每种类型的字符没有的话就随便加一个。

    现在只可能不满足长度条件,可以添加任意字符知道长度大于等于7。

    AC代码
    // Problem: New Password
    // Contest: Google Coding Competitions - Round C 2022 - Kick Start 2022
    // URL: https://codingcompetitions.withgoogle.com/kickstart/round/00000000008cb4d1/0000000000b20f15
    // Memory Limit: 1024 MB
    // Time Limit: 20000 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(...) ;
    #endif
    
    using i64 = int64_t;
    using u64 = uint64_t;
    
    void solve_case(int Case);
    
    int main(int argc, char* argv[]) {
      CPPIO;
      int T = 1;
      std::cin >> T;
      for (int t = 1; t <= T; ++t) {
        solve_case(t);
      }
      return 0;
    }
    
    void solve_case(int Case) {
      std::cout << "Case #" << Case << ": ";
      int n;
      std::cin >> n;
      std::string s;
      std::cin >> s;
    
      {
        bool flag = false;
        for (char ch : s) {
          if (std::islower(ch))
            flag = true;
        }
        if (!flag)
          s += 'a';
      }
    
      {
        bool flag = false;
        for (char ch : s) {
          if (std::isupper(ch))
            flag = true;
        }
        if (!flag)
          s += 'A';
      }
    
      {
        bool flag = false;
        for (char ch : s) {
          if (std::isdigit(ch))
            flag = true;
        }
        if (!flag)
          s += '0';
      }
    
      {
        bool flag = false;
        for (char ch : s) {
          if (ch == '#' || ch == '@' || ch == '*' || ch == '&')
            flag = true;
        }
        if (!flag)
          s += '#';
      }
      while (s.size() < 7)
        s += '*';
    
      std::cout << s << "\n";
    }
    
    

    Range Partition

    题意

    给一个\(1\)\(n\)的排列,让你将其划分为两部分,要求两部分元素和之比为\(x : y\)

    \(n\)至多为\(10^5\)

    思路

    总和可以算,比例已知,可以算出来其中一部分的元素之和。

    然后就是用\([1, n]\)去构造出这个元素和,经典结论,从大到小枚举,能用就用。

    AC代码
    // Problem: Range Partition
    // Contest: Google Coding Competitions - Round C 2022 - Kick Start 2022
    // URL: https://codingcompetitions.withgoogle.com/kickstart/round/00000000008cb4d1/0000000000b20deb
    // Memory Limit: 1024 MB
    // Time Limit: 5000 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(...) ;
    #endif
    
    using i64 = int64_t;
    using u64 = uint64_t;
    
    void solve_case(int Case);
    
    int main(int argc, char* argv[]) {
      CPPIO;
      int T = 1;
      std::cin >> T;
      for (int t = 1; t <= T; ++t) {
        solve_case(t);
      }
      return 0;
    }
    
    void solve_case(int Case) {
      std::cout << "Case #" << Case << ": ";
      int n, x, y;
      std::cin >> n >> x >> y;
    
      int s = (1 + n) * n / 2;
      if (s % (x + y) != 0) {
        std::cout << "IMPOSSIBLE\n";
        return;
      }
    
      i64 sa = i64(s) * x / (x + y);
      std::vector<int> ans;
      for (int i = n; i >= 1; --i) {
        if (sa >= i) {
          ans.push_back(i);
          sa -= i;
        }
      }
    
      std::cout << "POSSIBLE\n";
      std::cout << ans.size() << "\n";
      for (int i = 0; i < ans.size(); ++i)
        std::cout << ans[i] << " \n"[i + 1 == ans.size()];
    }
    
    

    Ants on a Stick

    题意

    \(n\)只蚂蚁,在长为\(L\)的树枝上运动。

    所有蚂蚁的移动速率恒定为\(1\),但是方向可能不同,两只蚂蚁相遇之后会立刻翻转方向,不会产生位移。

    树枝两侧都可以离开树枝,蚂蚁一旦走出树枝的范围就不会再对其他蚂蚁产生影响。

    给蚂蚁的初始位置和初始运动方向,要求给出蚂蚁离开树枝范围的顺序,相同时间离开的蚂蚁按下标升序。

    \(n\)至多为\(10^5\)

    思路

    观察:两只蚂蚁相遇其实相当于两只蚂蚁交换初始位置和方向,然后继续运动。

    由此可以推导出离开树枝的时间只和蚂蚁的初始位置和方向有关。

    首先按初始位置排序,左侧的蚂蚁如果向左走那就直接离开了,记录前面向右走的蚂蚁。

    假设现在遇到一只向左走的蚂蚁,观察可得:相当于把向左走的蚂蚁加入序列,然后将序列循环右移一个位置。此时最左侧的蚂蚁变成向左走的了,将其弹出序列。

    这里,循环右移过程中只交换初始位置和方向,不交换标号。

    模拟一遍得到最后的序列,然后就可以算出每个标号的蚂蚁离开树枝的时间,然后再排序一下就能得到答案。

    \(O(n)\)模拟实现循环右移,复杂度为\(O(n^2)\),可以过前两个点。用平衡树模拟区间操作复杂度为\(O(n \log n)\),可以过所有点。

    线性模拟实现
    // Problem: Ants on a Stick
    // Contest: Google Coding Competitions - Round C 2022 - Kick Start 2022
    // URL: https://codingcompetitions.withgoogle.com/kickstart/round/00000000008cb4d1/0000000000b209bc
    // Memory Limit: 1024 MB
    // Time Limit: 20000 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(...) ;
    #endif
    
    using i64 = int64_t;
    using u64 = uint64_t;
    
    void solve_case(int Case);
    
    int main(int argc, char* argv[]) {
      CPPIO;
      int T = 1;
      std::cin >> T;
      for (int t = 1; t <= T; ++t) {
        solve_case(t);
      }
      return 0;
    }
    
    void solve_case(int Case) {
      logd(Case);
      std::cout << "Case #" << Case << ": ";
      int n, l;
      std::cin >> n >> l;
    
      std::vector<std::array<int, 3>> a(n);
      for (int i = 0; i < n; ++i) {
        int p, d;
        std::cin >> p >> d;
        a[i] = {p, d, i};
      }
      std::sort(a.begin(), a.end());
    
      std::vector<int> q;
      for (int i = 0; i < n; ++i) {
        q.push_back(i);
    
        if (a[i][1] == 0) {
          logd(q);
          if (!q.empty()) {
            auto temp = a[q[q.size() - 1]];
            for (int i = q.size() - 1; i >= 1; --i) {
              a[q[i]][0] = a[q[i - 1]][0];
              a[q[i]][1] = a[q[i - 1]][1];
            }
            a[q[0]][0] = temp[0];
            a[q[0]][1] = temp[1];
            q.erase(q.begin());
          }
          logd(a, q);
        }
      }
    
      std::vector<int> t(n);
      for (int i = 0; i < n; ++i) {
        auto [p, d, id] = a[i];
        if (d == 0)
          t[id] = p;
        else
          t[id] = l - p;
      }
      logd(t);
    
      std::vector<int> id(n);
      std::iota(id.begin(), id.end(), 0);
      std::sort(id.begin(), id.end(), [&t](int x, int y) {
        if (t[x] == t[y])
          return x < y;
        return t[x] < t[y];
      });
      for (int i = 0; i < n; ++i)
        std::cout << id[i] + 1 << " \n"[i + 1 == n];
    }
    
    
    平衡树模拟实现
    // Problem: Ants on a Stick
    // Contest: Google Coding Competitions - Round C 2022 - Kick Start 2022
    // URL: https://codingcompetitions.withgoogle.com/kickstart/round/00000000008cb4d1/0000000000b209bc
    // Memory Limit: 1024 MB
    // Time Limit: 20000 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(...) ;
    #endif
    
    using i64 = int64_t;
    using u64 = uint64_t;
    
    void solve_case(int Case);
    
    int main(int argc, char* argv[]) {
      CPPIO;
      int T = 1;
      std::cin >> T;
      for (int t = 1; t <= T; ++t) {
        solve_case(t);
      }
      return 0;
    }
    
    std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());
    template <typename T>
    struct Treap {
      struct node {
        node *l, *r;
        unsigned rnd;
        T v;
        int sz;
        node(T _v) : l(NULL), r(NULL), rnd(rng()), sz(1), v(_v) {}
      };
    
      inline int get_size(node*& p) { return p ? p->sz : 0; }
    
      inline void push_up(node*& p) {
        if (!p)
          return;
        p->sz = get_size(p->l) + get_size(p->r) + 1;
      }
    
      node* root = NULL;
    
      node* merge(node* a, node* b) {
        if (!a)
          return b;
        if (!b)
          return a;
        if (a->rnd < b->rnd) {
          a->r = merge(a->r, b);
          push_up(a);
          return a;
        } else {
          b->l = merge(a, b->l);
          push_up(b);
          return b;
        }
      }
    
      void split_val(node* p, const T& k, node*& a, node*& b) {
        if (!p)
          a = b = NULL;
        else {
          if (p->v <= k) {
            a = p;
            split_val(p->r, k, a->r, b);
            push_up(a);
          } else {
            b = p;
            split_val(p->l, k, a, b->l);
            push_up(b);
          }
        }
      }
    
      void split_size(node* p, int k, node*& a, node*& b) {
        if (!p)
          a = b = NULL;
        else {
          if (get_size(p->l) < k) {
            a = p;
            split_size(p->r, k - get_size(p->l) - 1, a->r, b);
            push_up(a);
          } else {
            b = p;
            split_size(p->l, k, a, b->l);
            push_up(b);
          }
        }
      }
    
      void ins(int p, T val) {
        node *a, *b;
        split_size(root, p - 1, a, b);
        a = merge(a, new node(val));
        root = merge(a, b);
      }
    
      void del(T val) {
        node *a, *b, *c, *d;
        split_val(root, val, a, b);
        split_val(a, val - 1, c, d);
        node* e = d;
        d = merge(d->l, d->r);
        delete e;
        a = merge(c, d);
        root = merge(a, b);
      }
    
      T qry(int p) {
        node *a, *b, *c, *d;
        split_size(root, p - 1, a, b);
        // logd(get_size(a), get_size(b));
        split_size(b, 1, c, d);
        // logd(get_size(c), get_size(d));
        T result = c->v;
        b = merge(c, d);
        root = merge(a, b);
        return result;
      }
    
      void right_rotate(int l, int r) {
        node *a, *b, *c, *d;
        split_size(root, l - 1, a, b);
        split_size(b, r - l + 1, c, d);
    
        {
          node *e, *f;
          split_size(c, r - l, e, f);
          c = merge(f, e);
        }
    
        b = merge(c, d);
        root = merge(a, b);
      }
    
      void debug() {
        std::function<void(node*)> dfs = [&](node* p) {
          if (!p)
            return;
          dfs(p->l);
          std::cerr << to_string(p->v) << " ";
          dfs(p->r);
        };
        dfs(root);
        std::cerr << std::endl;
      }
    };
    
    void solve_case(int Case) {
      logd(Case);
      std::cout << "Case #" << Case << ": ";
      int n, L;
      std::cin >> n >> L;
    
      std::vector<std::array<int, 3>> a(n);
      for (int i = 0; i < n; ++i) {
        int p, d;
        std::cin >> p >> d;
        a[i] = {p, d, i};
      }
      std::sort(a.begin(), a.end());
    
      Treap<std::pair<int, int>> T;
      std::vector<int> id(n);
      for (int i = 0; i < n; ++i) {
        T.ins(i + 1, std::make_pair(a[i][0], a[i][1]));
        // T.debug();
        id[i] = a[i][2];
      }
    
      int l = 0;
      for (int i = 0; i < n; ++i) {
        if (a[i][1] == 0) {
          T.right_rotate(l + 1, i + 1);
          logd(l, i);
          // T.debug();
    
          ++l;
        }
      }
    
      std::vector<int> t(n);
      for (int i = 0; i < n; ++i) {
        logd(i);
        std::pair<int, int> x = T.qry(i + 1);
        logd(i, x);
        auto [p, d] = x;
        if (d == 0)
          t[id[i]] = p;
        else
          t[id[i]] = L - p;
      }
      logd(t);
    
      std::iota(id.begin(), id.end(), 0);
      std::sort(id.begin(), id.end(), [&t](int x, int y) {
        if (t[x] == t[y])
          return x < y;
        return t[x] < t[y];
      });
      for (int i = 0; i < n; ++i)
        std::cout << id[i] + 1 << " \n"[i + 1 == n];
    }
    
    

    Palindromic Deletions

    题意

    给定一个字符串\(s\),其长度为\(n\)。你需要完成下面这一操作共\(n\)次,问得分的期望。

    • 随机选择\(s\)一个字符,将其删去,得到一个新的\(s\)
    • 如果这个新的\(s\)是回文串,得一分
    思路

    第一个点随便暴力就过了。

    第二个点的话,观察可得:对于某个长度为\(k\)的回文子序列,他对答案的贡献为\(\frac{k! (n - k)!}{n!}\)。具体含义为先把\(n - k\)个字符删掉,共\((n - k)!\)种方案,然后这\(k\)个字符后续也要删掉,共\(k!\)种方案,因为完全随机,所以概率为\(\frac{k! (n - k)!}{n!}\)

    然后只需要求出长度为\(k\)的回文子序列有几个,再乘上概率就是期望了。可以借助DP求长度为\(k\)的回文子序列数量。

    假设\(dp_{k, l, r}\)表示\(s_{l}s_{l+1}\dots s_{r}\)构成的长度为\(k\)的回文子序列数量。长度为\(0\)和长度为\(1\)的可以直接算。

    对于\(k >= 2\),如果\(s_l = s_r\),那么就可以在所有由\(s_{l+1}s_{l+2}\dots s_{r-1}\)构成的回文子序列的基础上获得新的长度为\(k\)的回文子序列,数量为\(dp_{k - 2, l + 1, r - 1}\);否则没有新增的回文子序列,通过容斥可得这部分为\(dp_{k, l + 1, r} + dp_{k, l, r - 1} - dp_{k, l + 1, r - 1}\)

    然后就是\(O(n^3)\)DP跑出所有\(dp_{k, 0, n - 1}\)然后算期望了。

    AC代码
    // Problem: Palindromic Deletions
    // Contest: Google Coding Competitions - Round C 2022 - Kick Start 2022
    // URL: https://codingcompetitions.withgoogle.com/kickstart/round/00000000008cb4d1/0000000000b20d16
    // Memory Limit: 1024 MB
    // Time Limit: 30000 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(...) ;
    #endif
    
    using i64 = int64_t;
    using u64 = uint64_t;
    
    void solve_case(int Case);
    
    int main(int argc, char* argv[]) {
      CPPIO;
      int T = 1;
      std::cin >> T;
      for (int t = 1; t <= T; ++t) {
        solve_case(t);
      }
      return 0;
    }
    
    const int mod = 1e9 + 7;
    int qp(int a, int b) {
      int r = 1;
      while (b) {
        if (b & 1)
          r = i64(1) * r * a % mod;
        a = i64(1) * a * a % mod;
        b >>= 1;
      }
      return r;
    }
    
    int inv(int x) {
      return qp(x, mod - 2);
    }
    
    int slow(int n, std::string s) {
      int ans = 0;
      if (n <= 8) {
        std::function<void(std::string s, int)> dfs = [&](std::string s, int p) -> void {
          p = i64(1) * p * inv(s.size()) % mod;
          for (int i = 0; i < s.size(); ++i) {
            std::string t = s.substr(0, i) + s.substr(i + 1);
            // logd(p, t);
            std::string r = t;
            std::reverse(r.begin(), r.end());
            if (t == r) {
              ans = (ans + p) % mod;
            }
            if (!t.empty())
              dfs(t, p);
          }
        };
        dfs(s, 1);
      } else {
        assert(false);
      }
      return ans;
    }
    
    int fast(int n, std::string s) {
      int ans = 0;
    
      std::vector<int> fact(n + 1);
      fact[0] = 1;
      for (int i = 1; i <= n; ++i)
        fact[i] = i64(1) * fact[i - 1] * i % mod;
    
      std::vector<std::vector<std::vector<int>>> dp(
          n + 1, std::vector<std::vector<int>>(n, std::vector<int>(n, 0)));
    
      for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
          dp[0][i][j] = 1;
          dp[1][i][j] = std::max(0, j - i + 1);
        }
      }
      for (int k = 2; k <= n; ++k) {
        for (int len = 2; len <= n; ++len) {
          for (int i = 0; i + len - 1 < n; ++i) {
            int j = i + len - 1;
            dp[k][i][j] = (dp[k][i][j] + dp[k][i + 1][j]) % mod;
            dp[k][i][j] = (dp[k][i][j] + dp[k][i][j - 1]) % mod;
            dp[k][i][j] = ((dp[k][i][j] - dp[k][i + 1][j - 1]) % mod + mod) % mod;
            if (k >= 2 && s[i] == s[j])
              dp[k][i][j] = (dp[k][i][j] + dp[k - 2][i + 1][j - 1]) % mod;
          }
        }
      }
      logd(dp);
      for (int k = 0; k < n; ++k) {
        ans = (ans + i64(1) * dp[k][0][n - 1] * fact[k] % mod * fact[n - k] % mod) % mod;
      }
      ans = i64(1) * ans * inv(fact[n]) % mod;
      return ans;
    }
    
    void solve_case(int Case) {
      logd(Case);
      std::cout << "Case #" << Case << ": ";
      int n;
      std::cin >> n;
      std::string s;
      std::cin >> s;
    
      int ans = fast(n, s);
      std::cout << ans << "\n";
    #ifdef BACKLIGHT
      if (n <= 8) {
        int S = slow(n, s);
        logd(S, ans);
        // assert(S == ans);
      }
    #endif
    }
    
    
  • 相关阅读:
    Git常用命令
    Shell脚本学习
    Shell脚本学习
    Shell脚本学习
    Git ignore文件的用法
    RSA非对称加密算法
    C++ 标准库中的堆(heap)
    EM(Entity FrameWork)- code first , using in Visual stdio 2017
    C# 图片文字识别
    C# 调 C++ DLL 托管代码中释放非托管函数分配的内存
  • 原文地址:https://www.cnblogs.com/zengzk/p/16299759.html
Copyright © 2020-2023  润新知