Codeforces Round 972 (Div. 2)
A
如果有两个相同的字符,中间隔了若干个字符,形如 A******A
,那么会产生很多回文子序列。
为了避免这种情况,考虑将相同字符放在相邻位置,于是得到本题的正解构造:
首先将长度尽可能平分到 5 组,每组用相同字符重复 \(\lfloor\frac{n}{5}\rfloor\) 次或 \(\lceil\frac{n}{5}\rceil\) 次。
#include <bits/stdc++.h>
using namespace std;
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
#define IOS
#define MULTI
char mp[] = { 'a', 'e', 'i', 'o', 'u' };
void solve() {
int n;
cin >> n;
int m = n % 5, t = n / 5;
for (int i = 0; i < m; ++i)
for (int j = 0; j <= t; ++j)
cout << mp[i];
for (int i = m; i < 5; ++i)
for (int j = 0; j < t; ++j)
cout << mp[i];
cout << '\n';
}
int main() {
#ifdef IOS
ios::sync_with_stdio(false);
cin.tie(nullptr);
#endif
#ifdef MULTI
int TestCase = 1;
for (cin >> TestCase; TestCase--;) solve();
#else
solve();
#endif
return 0;
}
B
- David 被两个老师夹住:
那么答案非常显然,假设两个老师之间有 \(k\) 个空位,David 只能活 \(\lceil\frac{k}{2}\rceil\) 轮。
这个可以排序后用lower_bound
找到 David 左右的老师。 - David 没有被老师夹住:
那么 Daivd 显然会贪心地往边界跑,那么只需要求出离他最近的老师 \(x\) 的位置,答案就是 \(x - 1\) 或者 \(n - x - 1\)。
#include <bits/stdc++.h>
using namespace std;
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
#define IOS
#define MULTI
void solve() {
int n, m, q;
cin >> n >> m >> q;
vector<int> b(m);
for (int &x : b)
cin >> x;
sort(begin(b), end(b));
for (; q--;) {
int x;
cin >> x;
if (x < b[0]) {
cout << b[0] - 1 << '\n';
} else if (x > b.back()) {
cout << n - b.back() << '\n';
} else {
int p = upper_bound(begin(b), end(b), x) - begin(b);
int y = b[p] - b[p - 1] - 1;
cout << (y + 1) / 2 << '\n';
}
}
}
int main() {
#ifdef IOS
ios::sync_with_stdio(false);
cin.tie(nullptr);
#endif
#ifdef MULTI
int TestCase = 1;
for (cin >> TestCase; TestCase--;) solve();
#else
solve();
#endif
return 0;
}
C
给人一种显然 dp 的感觉。用 dp[i][j]
表示前 \(i\) 个字符串种选了若干个字符串,选完后 Narek 贪心匹配到了第 \(j~(0\le j<5)\) 个字符,此时的得分最大值。
那么只需要对每个字符串求出,假设之前已经匹配了 \(i~(0\le i<5)\) 个字符,经过这个字符串后,贪心匹配到了哪个字符(\(j\)),完美匹配了几次(\(k\))。用二元组 \(f(i) = (j, k)\) 记录即可转移。
#include <bits/stdc++.h>
using namespace std;
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
#define IOS
#define MULTI
template<typename T>
void chmax(T &x, T y) {
if (y > x)
x = y;
}
int mp[256];
void solve() {
int n, m;
cin >> n >> m;
vector<string> s(n);
vector<int> cnt(n);
vector<array<pair<int, int>, 5>> g(n);
for (int i = 0; i < n; ++i) {
cin >> s[i];
array<pair<int, int>, 5> res;
for (int j = 0; j < 5; ++j)
res[j] = { 0, j };
for (char ch : s[i]) {
cnt[i] += !!mp[ch];
for (int j = 0; j < 5; ++j) {
auto &[ans, cur] = res[j];
if (cur + 1 == mp[ch])
++cur;
if (cur == 5)
cur = 0, ans += 5;
}
}
g[i] = res;
// cout << "string " << i << "\n";
// for (int j = 0; j < 5; ++j) {
// auto [a, c] = res[j];
// cout << a << " " << c << '\n';
// }
}
vector<array<int, 5>> f(n + 1);
// f[i][j]: first i strings, matched to ch j, max
for (int i = 0; i <= n; ++i)
for (int j = 0; j < 5; ++j)
f[i][j] = -1e9;
f[0][0] = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < 5; ++j) {
// cout << "f[" << i << "][" << j << "] = " << f[i][j] << '\n';
auto [ans, cur] = g[i][j];
chmax(f[i + 1][cur], f[i][j] + 2 * ans - cnt[i]);
chmax(f[i + 1][j], f[i][j]);
}
}
int ans = 0;
for (int i = 0; i < 5; ++i)
chmax(ans, f[n][i]);
cout << ans << '\n';
}
int main() {
mp['n'] = 1;
mp['a'] = 2;
mp['r'] = 3;
mp['e'] = 4;
mp['k'] = 5;
#ifdef IOS
ios::sync_with_stdio(false);
cin.tie(nullptr);
#endif
#ifdef MULTI
int TestCase = 1;
for (cin >> TestCase; TestCase--;) solve();
#else
solve();
#endif
return 0;
}
D
需要利用一个非常常见的 gcd 的性质,就是前缀 gcd 不会有超过 \(\log V\) 种不同取值。
那么就可以暴力枚举了。先暴力枚举一个 \(l\),之后寻找所有有意义的 \(r\)。
考虑什么样的 \(r\) 有意义:当且仅当 \(r_1\) 和 \(r_2\) 的 \(\gcd(a_{l\cdots r_1})\neq \gcd(a_{l\cdots r_2})\) 或者 \(\gcd(a_{r_1\cdots a_n}) \neq \gcd(a_{r_2\cdots n})\) 时这两个 \(r\) 的答案不同。显然第一个是前缀 \(\gcd\) 不同,第二个是后缀 \(\gcd\) 不同,那么最多有 \(2\log V\) 种本质不同的 \(r\)。从 \(r = l\) 开始二分查找即可,利用 st 表可以做到 \(O(\log V)\) 区间 \(\gcd\)。总时间复杂度 \(O(n \log n\log V)\)。
#include <bits/stdc++.h>
using namespace std;
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
#define IOS
#define MULTI
int gcd(int x, int y, int z) {
return gcd(gcd(x, y), z);
}
void solve() {
int n;
cin >> n;
vector<int> a(n), b(n), lg(n + 1);
lg[0] = -1;
for (int i = 1; i <= n; ++i)
lg[i] = lg[i >> 1] + 1;
for (auto &x : a)
cin >> x;
for (auto &x : b)
cin >> x;
vector sta(20, vector<int>(n)), stb = sta;
for (int i = 0; i < n; ++i) {
sta[0][i] = a[i];
stb[0][i] = b[i];
}
for (int j = 1; (1 << j) <= n; ++j)
for (int i = 0; i + (1 << j) <= n; ++i) {
sta[j][i] = gcd(sta[j - 1][i], sta[j - 1][i + (1 << (j - 1))]);
stb[j][i] = gcd(stb[j - 1][i], stb[j - 1][i + (1 << (j - 1))]);
}
auto gcda = [&](int l, int r) -> int {
if (l > r || r >= n) return 0;
int len = lg[r - l + 1];
return gcd(sta[len][l], sta[len][r - (1 << len) + 1]);
};
auto gcdb = [&](int l, int r) -> int {
if (l > r || r >= n) return 0;
int len = lg[r - l + 1];
return gcd(stb[len][l], stb[len][r - (1 << len) + 1]);
};
i64 ans = 0, cnt = 0;
auto upd = [&](int x, int c) {
if (x > ans) {
ans = x;
cnt = c;
} else if (x == ans) cnt += c;
};
for (int L = 0; L < n; ++L) {
int d1 = a[L], d2 = b[L], sd1 = gcda(L + 1, n - 1), sd2 = gcdb(L + 1, n - 1);
int R = L;
while (R < n) {
int l = R, r = n - 1, mid, res = n;
// res: where something get wrong
while (l <= r) {
mid = (l + r) >> 1;
if (d1 != gcda(L, mid) || d2 != gcdb(L, mid)
|| sd1 != gcda(mid + 1, n - 1) || sd2 != gcdb(mid + 1, n - 1)) {
res = mid, r = mid - 1;
} else l = mid + 1;
}
int D1 = gcd(gcda(0, L - 1), d2, sd1);
int D2 = gcd(gcdb(0, L - 1), d1, sd2);
upd(D1 + D2, res - R), R = res;
d1 = gcda(L, R), d2 = gcdb(L, R);
sd1 = gcda(R + 1, n - 1), sd2 = gcdb(R + 1, n - 1);
}
}
cout << ans << " " << cnt << '\n';
}
int main() {
#ifdef IOS
ios::sync_with_stdio(false);
cin.tie(nullptr);
#endif
#ifdef MULTI
int TestCase = 1;
for (cin >> TestCase; TestCase--;) solve();
#else
solve();
#endif
return 0;
}
// D solution:O(n log^2 n)
// 枚举一个 l, 然后对于一个 r,不难发现后缀 gcd 不同的 r 最多有 log n 种,gcd([l, r]) 最多也有 log n 种
// 可以预处理出所有不同的后缀 r 的位置,值域 gcd([l, r]) 不同的位置,线段树上二分即可,时间复杂度 O(n log^2 n) 可以得到所有 l 和有意义的 r
// 可不可以不用线段树二分?可以,查询区间 gcd,这是 st 表的问题。不过时间复杂度还是 O(n log n log V)
E
没看。
本文来自博客园,作者:lingfunny,转载请注明原文链接:https://www.cnblogs.com/lingfunny/p/18414901