Educational Codeforces Round 183 (Rated for Div. 2) A~E
A - Candies for Nephews
模拟。
看 \(3\) 的余数。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::cout << (3 - n % 3) % 3 << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
B - Deck of Cards
模拟。
手玩可以发现,\(0/1\) 的顺序无所谓,对两边的影响是固定的,除开这些后,\(2\) 会影响剩余的数的两边,当 \(cnt_2 * 2 \ge n\) 的时候,中间全都不确定,否则就左右两边影响 \(cnt_2\) 个。
注意 \(k=n\) 特判,以及是 \(1\) 在上面,所以去掉上面的是从 \(1\) 开始,不是 \(n\)。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int n, k;
std::cin >> n >> k;
std::string s;
std::cin >> s;
if (k == n) {
std::cout << std::string(n, '-') << "\n";
return;
}
int cnt[3] {};
for (int i = 0; i < k; i += 1) {
cnt[s[i] - '0'] += 1;
}
std::string ans = "";
if (cnt[0]) {
ans += std::string(cnt[0], '-');
}
n -= cnt[0] + cnt[1];
if (2 * cnt[2] >= n) {
ans += std::string(n, '?');
} else {
ans += std::string(cnt[2], '?');
ans += std::string(n - 2 * cnt[2], '+');
ans += std::string(cnt[2], '?');
}
if (cnt[1]) {
ans += std::string(cnt[1], '-');
}
std::cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
C - Monocarp's String
前缀和,哈希。
将 \(a/b\) 视为 \(+1/-1\),那么问题就是问删除一个区间使得数组总和为 \(0\)。
定义 \(a_i\) 为 \(a/b\) 的贡献,若 \(\sum_{i=1}^na_i = d\),说明我们要找一段区间和等于 \(d\) 的删掉;定义 \(pre_i\) 表示前 \(i\) 个数的和,用哈希表 \(mp\) 记录每个 \(pre_i\) 的下标,如果 \(pre_i-d\) 出现过,说明 \(i-mp[pre_i-d] + 1\) 这一段就是要删掉的,比较一下取最小值即可。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::string s;
std::cin >> s;
int cnt[2] {};
for (int i = 0; i < n; i += 1) {
int c = s[i] - 'a';
cnt[c] += 1;
}
int d = cnt[0] - cnt[1];
map<int, int> mp;
mp[0] = 0;
int ans = n, sum = 0;
for (int i = 0; i < n; i += 1) {
if (s[i] == 'a') {
sum += 1;
} else {
sum -= 1;
}
if (mp.count(sum - d)) {
ans = min(ans, i - mp[sum - d] + 1);
}
mp[sum] = i + 1;
}
if (ans == n) {
ans = -1;
}
if (d == 0) {
ans = 0;
}
std::cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
D. Inversion Value of a Permutation
\(dp\)。
要求构造一个长度为 $ n $ 的排列,使得其逆序值恰好为 $ k $。逆序值定义为至少包含一个逆序对的连续子数组的数量。实际上,总子数组数为 $ \frac{n(n+1)}{2} $,减去没有逆序对的子数组数(即递增连续子数组数)即为逆序值。因此,问题转化为构造一个排列,使其递增连续子数组数 $ S = \frac{n(n+1)}{2} - k $。
若 $ S $ 无法表示为若干段长度 $ l_i $ 的平方和的一半之和(即 $ \sum \frac{l_i(l_i+1)}{2} = S $,其中 $ \sum l_i = n $),则输出 \(0\)。否则,将排列分成若干连续递增的段,段间递减排列即可。具体构造时,从大到小依次分配段内数字,每段内数字递增。
设 $ dp[x][i][j] $ 表示长度为 $ x $ 的排列中能否用若干段覆盖 $ i $ 个元素且递增子数组数为 $ j $,转移方程为:
然后根据预处理结果还原方案即可。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 30;
bool dp[N + 1][N + 1][500];
int len[N + 1][N + 1][500], lst[N + 1][N + 1][500];
void solve() {
int n, k;
std::cin >> n >> k;
int st = n * (n + 1) / 2 - k;
if (!dp[n][n][st]) {
std::cout << 0 << "\n";
} else {
std::vector<int> has;
int i = n;
while (i > 0) {
int l = len[n][i][st];
has.push_back(l);
st = lst[n][i][st];
i -= l;
}
std::reverse(has.begin(), has.end());
std::vector<int> ans;
int now = 0;
for (auto &d : has) {
int l = n - now - d + 1;
int r = n - now;
for (int num = l; num <= r; num++) {
ans.push_back(num);
}
now += d;
}
for (int i = 0; i < n; i += 1) {
std::cout << ans[i] << " \n"[i + 1 == n];
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
for (int x = 1; x <= N; x++) {
int y = x * (x + 1) / 2;
dp[x][0][0] = true;
for (int i = 0; i < x; i++) {
for (int j = 0; j <= y; j++) {
if (!dp[x][i][j]) continue;
for (int l = 1; l <= x - i; l++) {
int ni = i + l;
int nj = j + l * (l + 1) / 2;
if (nj <= y) {
dp[x][ni][nj] = true;
len[x][ni][nj] = l;
lst[x][ni][nj] = j;
}
}
}
}
}
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
E. Predicting Popularity
数据结构。
对于每个用户可以得到 \(p_i = \max(a_i-ac,0)+\max(d_i-dr,0)\),即满足该用户的最小受欢迎度。
如果当前电影观看人数为 \(p\),说明至少有 \(p\) 个 \(p_i< p\),一种方法显然是枚举 \(p\),去看 \(\sum [p_i< p]\) 的个数,反过来看,就是 \(p_i\) 对 \(p_i + 1\) 及以上的答案都有贡献;记 \(cnt_p\) 为满足每个 \(p\) 中 \(p_i<p\) 的个数,那么当 \(cnt_p \ge p\) 时,这个 \(p\) 就有可能是答案,为什么是有可能,因为要满足 \(p\) 是答案,那么前面的 \(1\sim p-1\) 都应该满足 \(cnt_i\ge i\),因此我们要的是最小的不满足 \(cnt_i\ge i\) 的位置,也就是 \(cnt_i-i<0\) 的第一个位置。
具体的,用线段树维护 \(cnt\) 数组,初始都减去一个 \(i\),每次 \(p_i\) 变化后就对相应的 \([p_i+1,n]\) 加减 \(1\),然后线段树上二分找第一个负数,因为答案可能为 \(0\),这里就整体偏移一位找第一个小于等于 \(0\) 的了。
参考[1]。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
template<class Info, class Tag>
struct LazySegmentTree {
int n;
std::vector<Info> info;
std::vector<Tag> tag;
LazySegmentTree() : n(0) {}
LazySegmentTree(int n_, Info v_ = Info()) {
init(n_, v_);
}
template<class T>
LazySegmentTree(std::vector<T> init_) {
init(init_);
}
void init(int n_, Info v_ = Info()) {
init(std::vector(n_, v_));
}
template<class T>
void init(std::vector<T> init_) {
n = init_.size();
info.assign(4 << std::__lg(n), Info());
tag.assign(4 << std::__lg(n), Tag());
std::function<void(int, int, int)> build = [&](int p, int l, int r) {
if (r - l == 1) {
info[p] = init_[l];
return;
}
int m = (l + r) / 2;
build(2 * p, l, m);
build(2 * p + 1, m, r);
pull(p);
};
build(1, 0, n);
}
void pull(int p) {
info[p] = info[2 * p] + info[2 * p + 1];
}
void apply(int p, const Tag &v) {
info[p].apply(v);
tag[p].apply(v);
}
void push(int p) {
apply(2 * p, tag[p]);
apply(2 * p + 1, tag[p]);
tag[p] = Tag();
}
void modify(int p, int l, int r, int x, const Info &v) {
if (r - l == 1) {
info[p] = v;
return;
}
int m = (l + r) / 2;
push(p);
if (x < m) {
modify(2 * p, l, m, x, v);
} else {
modify(2 * p + 1, m, r, x, v);
}
pull(p);
}
void modify(int p, const Info &v) {
modify(1, 0, n, p, v);
}
Info rangeQuery(int p, int l, int r, int x, int y) {
if (l >= y || r <= x) {
return Info();
}
if (l >= x && r <= y) {
return info[p];
}
int m = (l + r) / 2;
push(p);
return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m, r, x, y);
}
Info rangeQuery(int l, int r) {
return rangeQuery(1, 0, n, l, r);
}
void rangeApply(int p, int l, int r, int x, int y, const Tag &v) {
if (l >= y || r <= x) {
return;
}
if (l >= x && r <= y) {
apply(p, v);
return;
}
int m = (l + r) / 2;
push(p);
rangeApply(2 * p, l, m, x, y, v);
rangeApply(2 * p + 1, m, r, x, y, v);
pull(p);
}
void rangeApply(int l, int r, const Tag &v) {
return rangeApply(1, 0, n, l, r, v);
}
template<class F>
int findFirst(int p, int l, int r, int x, int y, F &&pred) {
if (l >= y || r <= x) {
return -1;
}
if (l >= x && r <= y && !pred(info[p])) {
return -1;
}
if (r - l == 1) {
return l;
}
int m = (l + r) / 2;
push(p);
int res = findFirst(2 * p, l, m, x, y, pred);
if (res == -1) {
res = findFirst(2 * p + 1, m, r, x, y, pred);
}
return res;
}
template<class F>
int findFirst(int l, int r, F &&pred) {
return findFirst(1, 0, n, l, r, pred);
}
template<class F>
int findLast(int p, int l, int r, int x, int y, F &&pred) {
if (l >= y || r <= x) {
return -1;
}
if (l >= x && r <= y && !pred(info[p])) {
return -1;
}
if (r - l == 1) {
return l;
}
int m = (l + r) / 2;
push(p);
int res = findLast(2 * p + 1, m, r, x, y, pred);
if (res == -1) {
res = findLast(2 * p, l, m, x, y, pred);
}
return res;
}
template<class F>
int findLast(int l, int r, F &&pred) {
return findLast(1, 0, n, l, r, pred);
}
};
struct Tag {
int add = 0;
void apply(const Tag &t) {
add += t.add;
}
};
constexpr int inf = 1E9;
struct Info {
int min = inf;
void apply(const Tag &t) {
min += t.add;
}
};
Info operator+(const Info &l, const Info &r) {
return {std::min(l.min, r.min)};
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int ac, dr;
std::cin >> ac >> dr;
int n;
std::cin >> n;
std::vector<int> a(n), d(n);
for(int i = 0; i < n; i += 1) {
std::cin >> a[i];
}
for(int i = 0; i < n; i += 1) {
std::cin >> d[i];
}
LazySegmentTree<Info, Tag> seg(n + 1);
for(int i = 0; i <= n + 1; i += 1) {
seg.modify(i, {-i});
}
for(int i = 0; i < n; i += 1) {
int p = std::max(a[i] - ac, 0) + std::max(d[i] - dr, 0);
seg.rangeApply(p, n + 1, {1});
}
int m;
std::cin >> m;
while(m--) {
int k, x, y;
std::cin >> k >> x >> y;
k--;
int p = std::max(a[k] - ac, 0) + std::max(d[k] - dr, 0);
seg.rangeApply(p, n + 1, {-1});
a[k] = x, d[k] = y;
p = std::max(a[k] - ac, 0) + std::max(d[k] - dr, 0);
seg.rangeApply(p, n + 1, {1});
auto res = seg.findFirst(0, n + 1, [](auto x){
return x.min <= 0;
});
std::cout << res << "\n";
}
return 0;
}

浙公网安备 33010602011771号