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
}

posted @ 2022-05-23 01:02  _Backl1ght  阅读(110)  评论(0编辑  收藏  举报