Educational Codeforces Round 78 (Div. 2)
比赛链接:https://codeforces.com/contest/1278
A - Shuffle Hashing
题意
给你两个由小写字母组成的字符串 p 和 h,问 h 中是否有连续子串可以由 p 打乱后生成。(1≤|p|, |h|≤100)
题解一
给字符串 p 和在 h 中的 p 长连续子串排序,比较是否有连续子串与 p 相等。
代码一
#include <bits/stdc++.h> #define sz(x) int(x.size()) #define all(x) x.begin(), x.end() using namespace std; int solve() { string p, h; cin >> p >> h; sort(all(p)); for (int i = 0; i + sz(p) <= sz(h); i++) { string sub_h = h.substr(i, sz(p)); sort(all(sub_h)); if (sub_h == p) return puts("YES"); } puts("NO"); } int main() { int t; cin >> t; while (t--) solve(); }
题解二
记录字符串 h 中每个位置 26 个字母的个数,比较字符串 h 中是否存在某个连续子串与字符串 p 中的字母个数相等。
代码二
#include <bits/stdc++.h> using namespace std; void solve() { string p, h; cin >> p >> h; vector<int> cnt_p(26, 0); vector<vector<int>> cnt_h(105, vector<int>(26, 0)); for (char c : p) { cnt_p[c - 'a']++; } for (int i = 1; i <= h.size(); i++) { cnt_h[i] = cnt_h[i - 1]; cnt_h[i][h[i - 1] - 'a']++; for (int j = 0; j < i; j++) { vector<int> t(26); for (int k = 0; k < 26; k++) { t[k] = cnt_h[i][k] - cnt_h[j][k]; } if (t == cnt_p) { cout << "YES" << "\n"; return; } } } cout << "NO" << "\n"; } int main() { int t; cin >> t; while (t--) solve(); }
B - A and B
题意
有正整数 a、b,第 i 次操作可以选择给一个数加 i,至少需要操作多少次才能使 a, b 相等。
题解
假设 b > a,若加上的数的总和小于 b - a 则一定无法使 a,b 相等,所以加上的数的总和需要大于等于 b - a。
若加上的总和大于 b - a,则多出来的这部分一定要可以平分为两份,即,给 a 加上 b - a + (sum - (b - a)) / 2,给 b 加上 (sum - (b - a)) / 2。
代码
#include <bits/stdc++.h> using namespace std; bool ok(int n, int dif) { long long sum = (n + 1LL) * n / 2; return sum >= dif and (sum - dif) % 2 == 0; } void solve() { int a, b; cin >> a >> b; int dif = abs(b - a); int ans = 0; while (!ok(ans, dif)) ++ans; cout << ans << "\n"; } int main() { int t; cin >> t; while (t--) solve(); }
C - Berry Jam
题意
有 2n 瓶草莓和蓝莓果酱,只能从一开始的中间处向左右连续一瓶一瓶地吃,问最少吃多少瓶果酱才能使剩下的草莓和蓝莓果酱瓶数相等。
题解
假设草莓比蓝莓多了 dif 瓶,向左连续吃的多了 l 瓶,向右连续吃的多了 r 瓶,即查找是否存在 l + r = dif 。
注意讨论左边或右边没吃的情况。
代码
#include <bits/stdc++.h> using namespace std; const int M = 2e5 + 100; int a[M]; void solve() { int n; cin >> n; int dif = 0; for (int i = 1; i <= 2 * n; i++) { cin >> a[i]; dif += (a[i] == 1 ? 1 : -1); } map<int, int> lf, rt; int sum = 0; lf[0] = n + 1;//若只吃右边就可以解决,则会有 dif - r = 0,此时查询时返回右边第一个端点即可 for (int i = n; i >= 1; i--) { sum += (a[i] == 1 ? 1 : -1); lf[sum] = max(i, lf[sum]);//对于左边来说,同一个差值离中间越近吃得越少,编号越大 } sum = 0; rt[0] = n;//若只吃左边就可以解决,则会有 dif - l = 0,此时查询时返回左边最后一个端点即可 for (int i = n + 1; i <= 2 * n; i++) { sum += (a[i] == 1 ? 1 : -1); rt[sum] = min(i, rt[sum]);//对于右边来说,同一个差值离中间越近吃得越少,编号越小 } int ans = INT_MAX; for (auto l : lf) { auto r = rt.find(dif - l.first); if (r != rt.end()) { ans = min(ans, r->second - l.second + 1); } } cout << ans << "\n"; } int main() { int t; cin >> t; while (t--) solve(); }
D - Segment Tree
题意
有 n 个位于 [1, 2n] 的区间,所有区间的端点各不相同(即 [1, 2n] 中的每个数都为某个区间的端点),如果有两个区间相交则这两个区间对应的两个编号间有一条边,问最后所有编号间能否组成一棵树。
题解
待填。
E - Tests for problem D
题意
与 D 题相反,给定一棵树找出 n 个满足 D 题意的区间。
题解
待填。

浙公网安备 33010602011771号