Educational Codeforces Round 178 (Rated for Div. 2)
A. Three Decks
题意:三个数\(a, b, c\),从\(c\)中拿一些数分给\(a, b\),能不能使得三个数相等。
首先把\(c\)变成\(b\),再把\(a\)变成\(b\),那么需要\(c - b > b - a\)。然后剩下\(c - b - (b - a)\),判断这个数能不能均分给三个数,也就是取模\(3\)等于零。
点击查看代码
void solve() {
int a, b, c;
std::cin >> a >> b >> c;
if (c - b < b - a || (c - (b - a) - b) % 3) {
std::cout << "NO\n";
} else {
std::cout << "YES\n";
}
}
B. Move to the End
题意:给你一个数组\(a\),对于一个后缀\([i, n]\),你可以把\([1, i - 1]\)一个数和\(a_i\)交换,求每个后缀的最大和。
记录前缀最大值,然后判断要不要当前数交换。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector<int> pre(n + 1), suf(n + 2);
for (int i = 0; i < n; ++ i) {
pre[i + 1] = std::max(pre[i], a[i]);
}
i64 sum = 0;
for (int i = n; i >= 1; -- i) {
sum += a[i - 1];
if (pre[i - 1] > a[i - 1]) {
std::cout << sum - a[i - 1] + pre[i - 1] << " \n"[i == 1];
} else {
std::cout << sum << " \n"[i == 1];
}
}
}
C. Card Game
题意:\(Alice, Bob\)两个人玩牌,有\(1\)到\(n\)种牌各一个,给出两个人开始分别有哪些牌。如果\(i > j\),则牌\(i\)大于牌\(j\)。特别的\(1 > n\)。每回合两个人轮流出牌,\(Alice\)先出,\(Bob\)后出,两个人出牌都是让对面知道牌的大小。没牌的输。求谁是赢家。
分类讨论。
如果\(1\)在\(Alice\)这里,那么如果\(n\)也在\(Alice\)这里或者\(Bob\)除了\(n\)没有其它牌则\(Alice\)赢,否则\(Bob\)赢。
如果\(1\)在\(Bob\)这里,那么\(Alice\)得有\(n\)这张牌,虽然他不能出这张牌,但如果\(n\)在\(Bob\)那就没法赢。然后因为\(Alice\)不能出\(n\),那么\(n-1\)就是最大的牌,那么\(Alice\)想赢\(n-1\)也得在他这里。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::string s;
std::cin >> s;
if (n == 2) {
if (s == "AB") {
std::cout << "Alice\n";
} else {
std::cout << "Bob\n";
}
return;
}
if (s[0] == 'A') {
if (s[n - 1] == 'A' || std::ranges::count(s, 'B') == 1) {
std::cout << "Alice\n";
} else {
std::cout << "Bob\n";
}
} else {
if (s[n - 1] == 'B' || s[n - 2] == 'B') {
std::cout << "Bob\n";
} else {
std::cout << "Alice\n";
}
}
}
D. Array and GCD
题意:\(n\)个数,需要从中删\(m\)个数,使得剩下的\(n - m\)个数的和分配个长度为\(n - m\)的数组,满足每个数都大于等于\(2\)且任意两个数互质,不要求全部分完。求最小的\(m\)。
因为不要求分完,那么我们肯定选最大的\(n-m\)个数。
然后考虑什么数组两两互质,思考发现这些数组都可以减少总和变成全是质数的数组。那么我们选前\(n-m\)个最小的质数,它们要求的和最小。然后把\(a\)从小到大删,判断满不满足条件。
观察到\(n\)最大为\(4e5\),筛质数筛到\(6e6\)就满足有\(4e5\)个质数了。这个范围可以写出质数筛后手动调范围看质数个数得到。
点击查看代码
std::vector<int> primes, minp;
std::vector<i64> sum;
void sieve(int n) {
primes.clear();
minp.assign(n + 1, 0);
for (int i = 2; i <= n; ++ i) {
if (minp[i] == 0) {
primes.push_back(i);
minp[i] = i;
}
for (auto & p : primes) {
if (p * i > n) {
break;
}
minp[p * i] = p;
if (minp[i] == p) {
break;
}
}
}
int m = primes.size();
sum.assign(m + 1, 0);
for (int i = 0; i < m; ++ i) {
sum[i + 1] = sum[i] + primes[i];
}
}
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::ranges::sort(a, std::greater<int>());
std::vector<i64> pre(n + 1);
for (int i = 0; i < n; ++ i) {
pre[i + 1] = pre[i] + a[i];
}
for (int i = n; i >= 0; -- i) {
if (pre[i] >= sum[i]) {
std::cout << n - i << "\n";
return;
}
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int T = 1;
sieve(6e6);
std::cin >> T;
//scanf("%d", &T);
while (T -- ) {
solve();
}
return 0;
}
E. Unpleasant Strings
题意:给你一个只有字典序前\(k\)个小写字母组成的字符串,\(q\)次询问,每次给出一个字符串\(t\),求最少给\(t\)后面添加几个字典序前\(k\)的字符使得\(t\)不是\(s\)的子序列。
首先对于求子序列的匹配,我们可以用\(suf[i][j]\)表示\(s\)中第\(i\)位之后\(j\)下一次出现的位置,如果没有出现记为\(suf[i][j] = n + 1\)。这样\(t\)进行匹配的时候只需要\(i=0\)开始,然后一直跳\(suf[i][j]\)。最后跳到哪里就代表\(s\)可以匹配它到哪里。这样跳复杂度只和\(t\)有关。
然后我们可以从每个\(suf[i][j]\)向\(i\)连边,每条边代表\(i\)需要添加一个字符到\(suf[i][j]\)。那么从\(n + 1\)跑\(bfs\),就可以得到从\(i\)开始最少添加几个字符可以到\(n+1\),也就是不匹配的位置。
综上所述,只需要预处理这两个东西,然后每次找到\(t\)在\(s\)中匹配到的位置,然后输出答案即可。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::string s;
std::cin >> s;
std::vector<std::array<int, 26>> suf(n + 1);
std::ranges::fill(suf[n], n + 1);
for (int i = n - 1; i >= 0; -- i) {
suf[i] = suf[i + 1];
suf[i][s[i] - 'a'] = i + 1;
}
std::vector<std::vector<int>> adj(n + 2);
for (int i = 0; i <= n; ++ i) {
for (int j = 0; j < k; ++ j) {
adj[suf[i][j]].push_back(i);
}
}
const int inf = 1e9;
std::vector<int> d(n + 2, inf);
d[n + 1] = 0;
std::queue<int> q;
q.push(n + 1);
while (q.size()) {
int u = q.front(); q.pop();
for (auto & v : adj[u]) {
if (d[v] > d[u] + 1) {
d[v] = d[u] + 1;
q.push(v);
}
}
}
int Q;
std::cin >> Q;
while (Q -- ) {
std::string t;
std::cin >> t;
int p = 0;
for (auto & c : t) {
if (p == n + 1) {
break;
}
p = suf[p][c - 'a'];
}
std::cout << d[p] << "\n";
}
}