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. 对于每个 \(1 \le i < n\),有 \(a_i < a_{i+1}\)\(b_i < b_{i+1}\)
  2. 对于每个 \(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;
}
posted @ 2025-06-18 02:48  __insomnium  阅读(599)  评论(5)    收藏  举报