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
,R
和C
的字符串,奇数次操作的时候可以将一个等于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";
}