其他分享
首页 > 其他分享> > AtCoder Regular Contest 140

AtCoder Regular Contest 140

作者:互联网

A - Right String

题意

给定一个长度为\(n\)的字符串\(s\),每次可以修改其中一个字符至任意字符,至多使用\(k\)次修改。

记字符串的权重为\(\#\{f(s, i)\}\),\(f(s, i)\)表示将\(s\)循环左移\(i\)次。

其中\(n, k \le 2000\)。

思路

观察可得:答案为字符串的最小整周期。

由于\(n\)比较小,可以直接枚举周期大小,也就是\(n\)的因数,然后再线性检查是否可行。

对于周期\(p\),假设循环节为\(r\),\(r_i\)的选择就是贪心选所有\(s_{i + kp}\)中出现次数最多的。

AC代码
// Problem: A - Right String
// Contest: AtCoder - AtCoder Regular Contest 140
// URL: https://atcoder.jp/contests/arc140/tasks/arc140_a
// 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(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main() {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    solve_case(t);
  }
  return 0;
}

void solve_case(int Case) {
  int n, k;
  std::string s;
  std::cin >> n >> k;
  std::cin >> s;

  int ans = n;

  auto check = [&](int l) {
    int temp = 0;

    std::vector<std::vector<int>> c(l, std::vector<int>(26, 0));
    for (int i = 0; i < n; ++i) {
      c[i % l][s[i] - 'a']++;
    }

    for (int i = 0; i < l; ++i) {
      int sum = std::accumulate(c[i].begin(), c[i].end(), 0);
      int ma = *std::max_element(c[i].begin(), c[i].end());
      temp += sum - ma;
    }

    if (temp <= k)
      ans = std::min(ans, l);
  };

  for (int l = 1; l * l <= n; ++l) {
    if (n % l == 0) {
      check(l);
      if (l * l != n)
        check(n / l);
    }
  }

  std::cout << ans << "\n";
}

B - Shorten ARC

题意

给定一个长度为\(n\),只包含A,RC的字符串,奇数次操作的时候可以将一个等于ARC的子串替换成R,偶数次操作的时候可以将一个ARC替换成AC

问最大操作次数。

其中\(n \le 2 \times 10^5\)。

思路

形如A...ARC...C这种的子串可以操作1次或者多次,把所有这种子串处理出来,将对应操作次数记录下来。

奇数次操作相当于减1,偶数次操作相当于删除元素。减1贪心用在大的元素上,删除贪心用在小的元素上,用一个std::multiset维护一下就可以了。

AC代码
// Problem: B - Shorten ARC
// Contest: AtCoder - AtCoder Regular Contest 140
// URL: https://atcoder.jp/contests/arc140/tasks/arc140_b
// 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(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main() {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    solve_case(t);
  }
  return 0;
}

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

  std::string s;
  std::cin >> s;

  std::vector<int> a;

  for (int i = 0; i < n; ++i) {
    if (s[i] != 'R')
      continue;

    int p, l = 0, r = 0;

    p = i - 1;
    while (p >= 0 && s[p] == 'A') {
      ++l;
      --p;
    }

    p = i + 1;
    while (p < n && s[p] == 'C') {
      ++r;
      ++p;
    }

    int c = std::min(l, r);
    if (c > 0)
      a.push_back(c);
  }

  if (a.empty()) {
    std::cout << "0\n";
    return;
  }

  int l = 1, r = std::accumulate(a.begin(), a.end(), 0), mid, ans = 0;
  while (l <= r) {
    mid = (l + r) >> 1;
    logd(l, r);

    bool can = true;

    std::multiset<int> st;
    st.clear();
    for (int i = 0; i < a.size(); ++i) {
      st.insert(a[i]);
    }

    logd(st);
    for (int i = 1; i <= mid; ++i) {
      if (st.empty()) {
        can = false;
        break;
      }

      if (i & 1) {
        auto it = --st.end();
        int c = *it;
        st.erase(it);
        --c;
        if (c > 0) {
          st.insert(c);
        }
      } else {
        auto it = st.begin();
        st.erase(it);
      }
      logd(i, st);
    }

    if (can)
      l = mid + 1, ans = mid;
    else
      r = mid - 1;
  }

  std::cout << ans << "\n";
}

C - ABS Permutation (LIS ver.)

题意

要求构造一个\((1, \dots, n)\)的排列\(p\),使得\(p_1 = x\)且\(a_i = |p_i - p_{i - 1}|\)的LIS最长。

其中\(n \le 2 \times 10^5\)。

思路

大概就是构造出一个形如\(x, x+1, x-1, x+2, x-2, \dots\)或者\(x, x-1, x+1, x-2, x+2, \dots\)这样的排列。

如果\(n\)为奇数,如果\(x = \lfloor \frac{n}{2} \rfloor + 1\),那么刚好能构造出LIS长度为\(n - 1\)的;否则只能构造出LIS长度为\(n - 2\)的,构造方法见后文。

如果\(n\)为偶数,如果\(x = \frac{n}{2}\)或者\(x =\frac{n}{2} + 1\),那么就能构造出LIS长度为\(n - 1\)的,这里要注意方向的选择;否则只能构造出长度为\(n - 2\)的,构造方法同上。

构造方法:将除了\(x\)之外的其他元素分为两组\(L\)和\(R\),然后两个数组大小向等或者\(L\)比\(R\)多一个元素,且\(L_1 < L_2 < \dots < L_n < R_1 < R_2 < \dots < R_m\),然后\(x, L_n, R_1, L_{n - 1}, R_2, \dots, L_1\)可以满足\(a\)的LIS长度为\(n - 2\)。

AC代码
// Problem: C - ABS Permutation (LIS ver.)
// Contest: AtCoder - AtCoder Regular Contest 140
// URL: https://atcoder.jp/contests/arc140/tasks/arc140_c
// 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(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main() {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    solve_case(t);
  }
  return 0;
}

void solve_case(int Case) {
  int n, x;
  std::cin >> n >> x;

  std::vector<int> p(n + 1);
  p[1] = x;

  if (n % 2 == 1 && x == n / 2 + 1) {
    for (int i = 2; i <= n; ++i) {
      if (i & 1)
        p[i] = p[i - 1] + (i - 1);
      else
        p[i] = p[i - 1] - (i - 1);
    }
  } else if (n % 2 == 0 && x == n / 2) {
    for (int i = 2; i <= n; ++i) {
      if (i & 1)
        p[i] = p[i - 1] - (i - 1);
      else
        p[i] = p[i - 1] + (i - 1);
    }
  } else if (n % 2 == 0 && x == n / 2 + 1) {
    for (int i = 2; i <= n; ++i) {
      if (i & 1)
        p[i] = p[i - 1] + (i - 1);
      else
        p[i] = p[i - 1] - (i - 1);
    }
  } else {
    std::vector<int> l, r;
    for (int i = 1; i <= n; ++i) {
      if (i == x)
        continue;
      if (l.size() < n / 2)
        l.push_back(i);
      else
        r.push_back(i);
    }
    logd(l, r);
    for (int i = 2, j = l.size() - 1; j >= 0; --j, i += 2) {
      logd(i, j);
      p[i] = l[j];
    }
    for (int i = 3, j = 0; j < r.size(); ++j, i += 2) {
      logd(i, j);
      p[i] = r[j];
    }
  }

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

  logd(p);
  for (int i = 1; i <= n; ++i)
    assert(1 <= p[i] && p[i] <= n);
}

D - One to One

题意

给一个长度为\(n\)的数组\(a_i\),\(a_i \ne -1\)表示\(i\)和\(a_i\)之间有一条边,\(a_i = -1\)表示\(a_i\)的可能在\([1, n]\)中任取。

\(f(a)\)表示\(a\)对应的图中连通快的数目。

问所有可能情况中\(f(a)\)的和。

其中\(n \le 2000\)。

思路

建出来的图中,一个连通块对应一个环,然后可以从数连通块转化为数环。

首先是\(a_i \ne -1\)的边组成的环,假设共有\(C\)个,且有\(D\)个\(i\)使得\(a_i = -1\),则这部分的贡献为\(C \times n^D\)。

此时,除了环之外其余连通块都是树,假设有\(K\)棵树,第\(i\)棵树的大小为\(B_i\)。考虑选择其中\(k\)个棵树链接起来组成一个环,假设选取了点\(x_i, 1 \le i \le k\),则这个环的种类有\((k - 1)!\prod_{i = 1}^{k} B_{x_i}\)。假设前\(i\)个点,大小为\(j\)的环共有\(dp_{i, j}\)类,则\(dp_{i, j} = dp_{i - 1, j} + dp_{i - 1, j - 1} \times B_i\)。

然后考虑大小为\(k\)的环对答案的贡献,其值为\(dp_{K, k} \times (k - 1)! \times n^{K - k}\)。

然后就没了。

AC代码
// Problem: D - One to One
// Contest: AtCoder - AtCoder Regular Contest 140
// URL: https://atcoder.jp/contests/arc140/tasks/arc140_d
// 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(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main() {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    solve_case(t);
  }
  return 0;
}

struct DSU {
  int n;
  std::vector<int> f, tag, sz;

  DSU(int _n) : n(_n), f(n), tag(n), sz(n) {
    for (int i = 0; i < n; ++i) {
      f[i] = i;
      tag[i] = 0;
      sz[i] = 1;
    }
  }

  int leader(int x) {
    if (f[x] == x)
      return x;
    f[x] = leader(f[x]);
    return f[x];
  }

  bool same(int x, int y) { return leader(x) == leader(y); }

  void merge(int x, int y) {
    x = leader(x);
    y = leader(y);
    if (x == y)
      return;
    f[x] = y;
    sz[y] += sz[x];
  }

  void set(int x) { tag[x] = 1; }
};

const int mod = 998244353;

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

  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<int> power(n + 1);
  power[0] = 1;
  for (int i = 1; i <= n; ++i)
    power[i] = i64(1) * power[i - 1] * n % mod;

  std::vector<int> a(n);
  for (int i = 0; i < n; ++i) {
    std::cin >> a[i];
    if (a[i] != -1)
      --a[i];
  }

  DSU dsu(n);
  for (int i = 0; i < n; ++i) {
    if (a[i] != -1) {
      if (dsu.same(i, a[i]))
        dsu.set(dsu.leader(i));
      else
        dsu.merge(i, a[i]);
    }
  }

  int C = 0;
  std::vector<int> B(1);
  for (int i = 0; i < n; ++i) {
    if (i != dsu.f[i])
      continue;
    if (dsu.tag[i]) {
      ++C;
      continue;
    }
    B.push_back(dsu.sz[i]);
  }
  logd(B);

  int D = 0;
  for (int i = 0; i < n; ++i) {
    if (a[i] == -1) {
      ++D;
    }
  }
  // cycle constructed by fixed edges
  int fixed = i64(1) * power[D] * C % mod;

  // cycle constructed by mutable edges
  int k = B.size() - 1;
  std::vector<std::vector<int> > dp(k + 1, std::vector<int>(k + 1, 0));
  for (int i = 0; i <= k; ++i)
    dp[i][0] = 1;
  for (int i = 1; i <= k; ++i) {
    for (int j = 0; j <= i; ++j) {
      dp[i][j] = (dp[i - 1][j] + i64(1) * dp[i - 1][j - 1] * B[i] % mod) % mod;
    }
  }
  int dynamic = 0;
  for (int i = 1; i <= k; ++i) {
    dynamic =
        (dynamic + i64(1) * fact[i - 1] * power[k - i] % mod * dp[k][i] % mod) %
        mod;
  }
  int ans = (fixed + dynamic) % mod;
  std::cout << ans << "\n";
}

标签:std,AtCoder,140,Contest,int,cin,++,logd,Regular
来源: https://www.cnblogs.com/zengzk/p/16275498.html