Codeforces Round 1032 (Div. 3)
A. Letter Home
给定一个由互不相同的整数构成的数组 \(x_1, x_2, \dots, x_n\),以及一个整数 \(s\)。
你一开始位于 X 轴上的位置 \(pos = s\)。每一步你可以执行以下两种操作之一(只能选一种):
- 从当前位置 \(pos\) 移动到 \(pos + 1\);
- 从当前位置 \(pos\) 移动到 \(pos - 1\)。
一次移动序列被称为“成功的”,当且仅当在整个过程中,你至少访问过一次数组中每个 \(x_i\) 所对应的位置。注意:初始位置 \(pos = s\) 也算作已访问。
你的任务是:计算任意一种成功的移动序列所需的最少步数。
如果 \(s\) 在左端点和右端点中间,要么先走到左端点再走到右端点,要么先走到右端点再走到左端点,否则就是直接走到距离最远的点。
代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 2000008, MOD = 998244353, INF = 0x3f3f3f3f;
ll res;
int n, m, cnt, w[N];
int main() {
int T;
cin >> T;
while (T--) {
cin >> n >> m;
for (int i = 1; i < n + 1; i++) scanf("%d", w + i);
sort(w + 1, w + n + 1);
if (m <= w[1]) printf("%d\n", w[n] - m);
else if (m >= w[n]) printf("%d\n", m - w[1]);
else printf("%d\n", w[n] - w[1] + min(m - w[1], w[n] - m));
}
return 0;
}
B. Above the Clouds
给定一个长度为 \(n\) 的字符串 \(s\),该字符串仅由小写拉丁字母组成。请判断是否存在三个非空字符串 \(a\)、\(b\) 和 \(c\),满足以下条件:
- \(a + b + c = s\),也就是说,将 \(a\)、\(b\) 和 \(c\) 依次连接后等于字符串 \(s\);
- 字符串 \(b\) 是字符串 \(a + c\) 的一个子串(即,\(b\) 出现在 \(a\) 和 \(c\) 拼接后的字符串中)。
如果存在除首尾以外的字符在字符串中出现至少两次则存在,否则不存在。
代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 2000008, MOD = 998244353, INF = 0x3f3f3f3f;
ll res;
int n, m, cnt, w[N];
char s[N];
int c[N];
void solve() {
for (int i = 'a'; i <= 'z'; i++) c[i] = 0;
for (int i = 1; i < n + 1; i++) c[s[i]]++;
for (int i = 2; i < n; i++)
if (c[s[i]] > 1) {
puts("Yes");
return;
}
puts("No");
}
int main() {
int T;
cin >> T;
while (T--) {
cin >> n;
scanf("%s", s + 1);
solve();
}
return 0;
}
C. Those Who Are With Us
给定一个 \(n\) 行 \(m\) 列的整数矩阵。第 \(i\) 行第 \(j\) 列的单元格中包含整数 \(a_{ij}\)。
你可以执行恰好一次如下操作:
- 选择两个数 \(1 \le r \le n\) 和 \(1 \le c \le m\);
- 对于所有满足 \(i = r\) 或 \(j = c\) 的单元格 \((i, j)\),将 \(a_{ij}\) 减去 \(1\)。
你的任务是找出执行完这一次操作后,矩阵 \(a\) 中可能出现的最小的最大值。
随便找一个值为最大值的位置,判断将其行作为 \(r\) 或者将其列作为 \(c\) 是否能使所有值为最大值的数 \(-1\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 2000008, MOD = 998244353, INF = 0x3f3f3f3f;
ll res;
int n, m, cnt, w[N];
void solve() {
cin >> n >> m;
vector<vector<int> > a(n + 2, vector<int>(m + 2));
int maxv = 0;
for (int i = 1; i < n + 1; i++)
for (int j = 1; j < m + 1; j++)
scanf("%d", &a[i][j]), maxv = max(maxv, a[i][j]);
for (int i = 1; i < n + 1; i++)
for (int j = 1; j < m + 1; j++)
if (a[i][j] == maxv) {
set<int> st;
for (int k = 1; k < n + 1; k++)
for (int l = 1; l < m + 1; l++)
if (a[k][l] == maxv && k != i)
st.insert(l);
if (st.size() <= 1) {
printf("%d\n", maxv - 1);
return;
}
st.clear();
for (int k = 1; k < n + 1; k++)
for (int l = 1; l < m + 1; l++)
if (a[k][l] == maxv && l != j)
st.insert(k);
if (st.size() <= 1) {
printf("%d\n", maxv - 1);
return;
}
printf("%d\n", maxv);
return;
}
}
int main() {
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}
D. 1709
给定两个整数数组 \(a_1, a_2, \dots, a_n\) 和 \(b_1, b_2, \dots, b_n\)。保证从 \(1\) 到 \(2 \cdot n\) 的每个整数恰好出现一次,且只出现在其中一个数组中。
你可以进行若干次操作(也可以不进行),目的是使得以下两个条件都满足:
- 对于每个 \(1 \le i < n\),有 \(a_i < a_{i+1}\) 且 \(b_i < b_{i+1}\);
- 对于每个 \(1 \le i \le n\),有 \(a_i < b_i\)。
你每次操作可以进行以下三种操作之一:
- 选择一个下标 \(1 \le i < n\),交换 \(a_i\) 和 \(a_{i+1}\) 的值;
- 选择一个下标 \(1 \le i < n\),交换 \(b_i\) 和 \(b_{i+1}\) 的值;
- 选择一个下标 \(1 \le i \le n\),交换 \(a_i\) 和 \(b_i\) 的值。
你不需要最小化操作次数,但总操作次数不能超过 1709 次。请找出任意一个满足条件的操作序列。
先从大到小将 \(a,b\) 中的每一个数交换到排序后对应的位置,再交换 \(a,b\) 间 \(a_i > b_i\) 的位置。操作次数最多为 \(\frac{n \times (n - 1)}{2} \times 2 + n\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 2000008, MOD = 998244353, INF = 0x3f3f3f3f;
ll res;
int n, m, cnt, w[N];
int a[50], b[50];
int pa[50], pb[50];
void solve() {
vector<pii> ans;
for (int i = 1; i < n + 1; i++) pa[i] = a[i], pb[i] = b[i];
sort(pa + 1, pa + n + 1);
sort(pb + 1, pb + n + 1);
for (int i = n; i; i--) {
for (int j = 1; j < n; j++)
if (a[j] == pa[i]) {
int now = j;
while (now != i) {
ans.push_back({1, now});
swap(a[now], a[now + 1]);
now++;
}
break;
}
}
for (int i = n; i; i--) {
for (int j = 1; j < n; j++)
if (b[j] == pb[i]) {
int now = j;
while (now != i) {
ans.push_back({2, now});
swap(b[now], b[now + 1]);
now++;
}
break;
}
}
for (int i = 1; i < n + 1; i++)
if (a[i] > b[i]) {
swap(a[i], b[i]);
ans.push_back({3, i});
}
printf("%d\n", ans.size());
for (auto u : ans) printf("%d %d\n", u.first, u.second);
}
int main() {
int T;
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i < n + 1; i++) scanf("%d", a + i);
for (int i = 1; i < n + 1; i++) scanf("%d", b + i);
solve();
}
return 0;
}
E. Sponsor of Your Problems
对于两个整数 \(a\) 和 \(b\),我们定义 \(f(a, b)\) 为它们的十进制表示中相同位置上数字相同的位数。例如:
- \(f(12, 21) = 0\)
- \(f(31, 37) = 1\)
- \(f(19891, 18981) = 2\)
- \(f(54321, 24361) = 3\)
现在给定两个整数 \(l\) 和 \(r\),它们的十进制表示长度相同。你需要在所有满足 \(l \le x \le r\) 的整数 \(x\) 中,找到使得 \(f(l, x) + f(x, r)\) 最小的那个值,并输出这个最小值。
分类讨论,从高位到低位考虑,若第 \(i\) 位 \(l_i = r_i\) , 那么 \(x_i\) 只能等于 \(l_i\),若 \(r_i - l_i > 1\),\(x_i\) 可以取 \(l_i + 1\),并且之后的每一位可以任取,若 \(r_i - l_i = 1\),\(x_i\) 可以取 \(l_i\) 或 \(r_i\),若 \(x_i\) 取 \(l_i\),对于后续的每一位 \(j\),若 \(l_j = 9\) 则 \(x_j\) 只能取 \(9\),否则 \(x_i\) 可以任取一个大于 \(l_j\) 的数,且之后的每一位可以任取,\(x_i\) 取 \(r_i\) 同理。
代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 2000008, MOD = 998244353, INF = 0x3f3f3f3f;
ll res;
int n, m, cnt, w[N];
int l, r;
void solve() {
vector<int> a, b;
while (l) a.push_back(l % 10), b.push_back(r % 10), l /= 10, r /= 10;
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
for (int i = 0; i < a.size(); i++) {
if (a[i] != b[i]) {
if (b[i] - a[i] > 1) {
printf("%d\n", i << 1);
return;
} else {
int v = 1, t = 1;
for (int j = i + 1; j < a.size(); j++) {
if (a[j] == 9) v += 1 + (b[j] == 9);
else {
if (a[j] == 8 && b[j] == 9) v++;
break;
}
}
for (int j = i + 1; j < a.size(); j++) {
if (b[j] == 0) t += 1 + (a[j] == 0);
else {
if (b[j] == 1 && a[j] == 0) t++;
break;
}
}
printf("%d\n", min(v, t) + (i << 1));
return;
}
}
}
printf("%d\n", a.size() << 1);
}
int main() {
int T;
cin >> T;
while (T--) {
cin >> l >> r;
solve();
}
return 0;
}
F. Yamakasi
给定一个整数数组 \(a_1, a_2, \ldots, a_n\) 和两个整数 \(s\) 和 \(x\)。请计算数组中满足以下条件的子段个数:
子段的元素和等于 \(s\),且子段中的最大值等于 \(x\)。
更正式地,计算满足 \(1 \le l \le r \le n\) 的所有区间对 \((l, r)\) 的数量,使得:
- \(a_l + a_{l+1} + \cdots + a_r = s\)。
- \(\max(a_l, a_{l+1}, \ldots, a_r) = x\)。
如果没有最大值的约束,我们可以用 \(map\) 维护每个前缀和的出现次数,在遍历时累加每个位置上当前前缀和 \(sum - s\) 在 \(map\) 中的出现次数。多出最大值的约束后,还需要维护最后一个值大于 \(x\) 的位置 \(gp\) 和最后一个值为 \(x\) 的位置 \(ep\),\(map\) 需要数组维护每个前缀值每一次出现的位置。在遍历每个位置时计算前缀和为 \(sum - s\) 中第一个大于 \(ep\) 的位置在数组中的下标和第一个大于 \(gp\) 的位置在数组中的下标,累加上两个下标的差值。
代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 2000008, MOD = 998244353, INF = 0x3f3f3f3f;
ll res;
int n, m, cnt, w[N];
ll s, x;
void solve() {
map<ll, vector<int> > ma = {{0, {0}}};
ll sum = 0, res = 0;
int la = -INF, p = -INF;
for (int i = 1; i < n + 1; i++) {
sum += w[i];
if (w[i] == x) la = i;
else if (w[i] > x) p = i;
if (la != -INF && ma.count(sum - s) && la >= p) {
auto t = lower_bound(ma[sum - s].begin(), ma[sum - s].end(), la);
auto u = lower_bound(ma[sum - s].begin(), ma[sum - s].end(), p);
res += t - u;
}
ma[sum].push_back(i);
}
printf("%lld\n", res);
}
int main() {
int T;
cin >> T;
while (T--) {
cin >> n >> s >> x;
for (int i = 1; i < n + 1; i++) scanf("%d", w + i);
solve();
}
return 0;
}
G. Gangsta
给定一个长度为 \(n\) 的二进制字符串 \(s_1s_2\ldots s_n\)。一个字符串称为二进制字符串,当且仅当它只包含 \(0\) 和 \(1\)。
对于任意字符串 \(p\),定义函数 \(f(p)\) 表示在字符串 \(p\) 中出现次数最多的某个字符的出现次数。例如,\(f(00110)=3\),\(f(01)=1\)。
你的任务是计算所有满足 \(1 \le l \le r \le n\) 的区间 \([l, r]\) 中子串 \(s_ls_{l+1}\ldots s_r\) 的 \(f\) 值之和。
跟上一题类似的前缀和,设 \(d_i\) 为前缀 \([1, i]\) 中 \(1\) 的数量减去 \(0\) 的数量,维护每一个 \(d_i\) 的出现次数以及这些前缀中 \(1\) 的总个数。在遍历到位置 \(i\) 时,以 \(i\) 作为区间右端点且区间中 \(1\) 的数量大于等于 \(0\) 的数量的区间数量等于 \(d_j \leq d_i\) 的前缀个数,\(1\) 的总个数为区间数量乘以 \([1,i]\) 中 \(1\) 的个数减去所有满足 \(d_j \leq d_i\) 的前缀中 \(1\) 的总和。\(0\) 的个数大于 \(1\) 的个数的区间同理。
代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 2000008, MOD = 998244353, INF = 0x3f3f3f3f;
ll res;
int n, m, cnt, w[N];
char s[N];
struct ST {
struct Node {
int l, r;
ll sum, c;
};
vector<Node> tr;
ST(ST&& other) noexcept : tr(move(other.tr)) { }
ST (int l, int r) : tr(vector<Node>(((r - l + 10) << 2) + 10)) {
build(1, l, r);
}
ST& operator=(ST&& other) noexcept {
if (this != &other) {
tr = move(other.tr);
}
return *this;
}
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
tr[u].c = tr[u << 1].c + tr[u << 1 | 1].c;
}
void build(int u, int l, int r) {
tr[u] = {l, r};
if (l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}
ll query(int u, int l, int r, bool flag) {
if (l > r) return 0;
if (tr[u].l >= l && tr[u].r <= r) return flag ? tr[u].sum : tr[u].c;
int mid = tr[u].l + tr[u].r >> 1;
ll res = 0;
if (l <= mid) res += query(u << 1, l, r, flag);
if (r > mid) res += query(u << 1 | 1, l, r, flag);
return res;
}
void modify(int u, int x, ll v) {
if (tr[u].l == tr[u].r) tr[u].c++, tr[u].sum += v;
else {
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);
}
}
};
void solve() {
res = 0;
ST tr(-n, n);
tr.modify(1, 0, 0);
int t = 0, c = 0;
for (int i = 1; i < n + 1; i++) {
t += s[i] == '1' ? 1 : -1;
c += s[i] == '1';
res -= tr.query(1, -n, t, 1);
res += tr.query(1, -n, t, 0) * c;
tr.modify(1, t, c);
}
t = 0, c = 0;
tr = ST(-n - 1, n);
tr.modify(1, 0, 0);
for (int i = 1; i < n + 1; i++) {
t += s[i] == '0' ? 1 : -1;
c += s[i] == '0';
res -= tr.query(1, -n - 1, t - 1, 1);
res += tr.query(1, -n - 1, t - 1, 0) * c;
tr.modify(1, t, c);
}
printf("%lld\n", res);
}
int main() {
int T;
cin >> T;
while (T--) {
cin >> n;
scanf("%s", s + 1);
solve();
}
return 0;
}
H. Ice Baby
一个整数数组 \(a_1, a_2, \ldots, a_n\) 的最长不下降子序列,指的是一个索引序列 \(1 \le i_1 < i_2 < \ldots < i_k \le n\),使得 \(a_{i_1} \le a_{i_2} \le \ldots \le a_{i_k}\) 成立。该序列的长度定义为其中元素的个数。例如,数组 \(a=[3,1,4,1,2]\) 的最长不下降子序列长度为 \(3\)。
现在给你两个整数数组 \(l_1, l_2, \ldots, l_n\) 和 \(r_1, r_2, \ldots, r_n\)。对于每个 \(1 \le k \le n\),解决以下问题:
考虑所有长度为 \(k\) 的整数数组 \(a\),使得对于每个 \(1 \le i \le k\),都有 \(l_i \le a_i \le r_i\)。你需要求出在所有这些数组中,最长不下降子序列的最大可能长度。
显然一定存在一个数组 \(a\),它的最长不下降子序列长度达到最大值,并且对于每个 \(1 \le i \le k\),\(a_i\) 要么等于下界 \(l_i\),要么等于之前某个位置 \(j < i\) 的值 \(a_j\)。若取 \(l_i\),则以 \(l_i\) 结尾的最长不下降子序列长度为区间 \([1, i - 1]\) 中结尾值小于等于 \(l_i\) 的最长不下降子序列长度 \(+1\),若不取 \(l_i\) 取 \(x\),则以 \(x\) 结尾的最长不下降子序列长度为区间 \([1, i- 1]\) 中以 \(x\) 结尾的最长不下降子序列长度 \(+1\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 2000008, MOD = 998244353, INF = 0x3f3f3f3f;
ll res;
int n, m, cnt, w[N];
vector<int> num;
inline int find(int x) { return lower_bound(num.begin(), num.end(), x) - num.begin(); }
int l[N], r[N];
struct ST {
struct Node {
int l, r;
ll max, flag;
};
vector<Node> tr;
ST(ST&& other) noexcept : tr(move(other.tr)) { }
ST (int l, int r) : tr(vector<Node>(((r - l + 10) << 2) + 10)) {
build(1, l, r);
}
ST& operator=(ST&& other) noexcept {
if (this != &other) {
tr = move(other.tr);
}
return *this;
}
void pushup(int u) { tr[u].max = max(tr[u << 1].max, tr[u << 1 | 1].max); }
void pushdown(Node& u, Node& l, Node& r) {
l.flag += u.flag, r.flag += u.flag;
l.max += u.flag, r.max += u.flag;
u.flag = 0;
}
void pushdown(int u) { if (tr[u].flag) pushdown(tr[u], tr[u << 1], tr[u << 1 | 1]); }
void build(int u, int l, int r) {
tr[u] = {l, r};
if (l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}
ll query(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r) return tr[u].max;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
ll res = 0;
if (l <= mid) res = query(u << 1, l, r);
if (r > mid) res = max(res, query(u << 1 | 1, l, r));
return res;
}
void modify(int u, int l, int r, ll v) {
if (tr[u].l >= l && tr[u].r <= r) tr[u].flag += v, tr[u].max += v;
else {
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) modify(u << 1, l, r, v);
if (r > mid) modify(u << 1 | 1, l, r, v);
pushup(u);
}
}
void modify(int u, int x, ll v) {
if (tr[u].l == tr[u].r) tr[u].max = v;
else {
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);
}
}
};
void solve() {
for (int i = 1; i < n + 1; i++) l[i] = find(l[i]), r[i] = find(r[i]);
ST tr(0, num.size());
for (int i = 1; i < n + 1; i++) {
int c = tr.query(1, 0, l[i]) + 1;
tr.modify(1, l[i], c);
if (l[i] != r[i]) tr.modify(1, l[i] + 1, r[i], 1);
printf("%d ", tr.tr[1].max);
}
puts("");
}
int main() {
int T;
cin >> T;
while (T--) {
cin >> n;
num.clear();
for (int i = 1; i < n + 1; i++) scanf("%d%d", l + i, r + i), num.push_back(l[i]), num.push_back(r[i]);
sort(num.begin(), num.end());
num.erase(unique(num.begin(), num.end()), num.end());
solve();
}
return 0;
}

浙公网安备 33010602011771号