跳转至

ABC384(A-G)

A - aaaadaa

题目大意

给你一个长度为 \(N\) 的字符串 \(S\) 以及两个字符 \(c_1\)\(c_2\),将 \(S\) 中所有不是 \(c_1\) 的字符替换成 \(c_2\)

参考代码
#include <iostream>
#include <string>

using namespace std;

int main()
{
    int n;
    char c1, c2;
    string s;
    cin >> n >> c1 >> c2 >> s;
    for(auto ch : s)
        cout << (ch == c1 ? c1 : c2);
    return 0;
}

B - ARC Division

题目大意

ARC 有两个级别:

  • Div. 1:所有积分位于\([1600, 2799]\) 的参赛者的表现都会影响积分。
  • Div. 2:所有积分位于\([1200, 2399]\) 的参赛者的表现都会影响积分。

你打算参加 \(N\) 场 ARC,第 \(i\) 场的级别是 Div. \(D_i\),在这场 ARC 中你的表现是 \(A_i\)

如果参赛前你的积分落在对应的区间上,那么你的积分将会受到比赛中的表现所影响,具体来说,如果参赛前你的积分是 \(T\),那么在比赛结束后你的积分会变成 \(T + A_i\)

问:\(N\) 场比赛结束后,你的积分是多少?

参考代码
#include <iostream>

using namespace std;

int main()
{
    int n, r;
    cin >> n >> r;

    auto div1 = [](int x) -> bool
    { return x >= 1600 && x <= 2799; };

    auto div2 = [](int x) -> bool
    { return x >= 1200 && x <= 2399; };

    while(n--)
    {
        int d, a;
        cin >> d >> a;
        if(d == 1)
        {
            if(div1(r))
                r += a;
        }
        else
        {
            if(div2(r))
                r += a;
        }
    }
    cout << r << endl;
    return 0;
}

C - Perfect Standings

题目大意

你举办了一场编程竞赛,里面有五道题 ABCDE,A 题分数是 \(a\),B 题分数是 \(b\),C 题分数是 \(c\),D 题分数是 \(d\),E 题分数是 \(e\),其中 \(100 \le a \le b \le c \le d \le e \le 2718\)

一共来了 \(31\) 位参赛选手,每位参赛选手都至少答对了 \(1\) 道题,且所有参赛选手的答对的题目情况都不一样。可以用一个字符串代表答对了哪些题,例如 ABCDE 表示答对了所有题目的选手,ACE 表示答对了 ACE 三道题的选手。你需要给这些选手答题情况的字符串排名:

  • 首先,根据答题情况的字符串,计算出选手的总得分,得分高的选手排名更考前。
  • 如果有同分的情况,则字符串字典序更小的排名更靠前,
解题思路

dfs 模板。

参考代码
#include <algorithm>
#include <iostream>
#include <utility>
#include <vector>

using namespace std;

int main()
{
    vector<pair<int, string>> ans;
    vector<int> score(5);
    for (int i = 0; i < 5; i++)
        cin >> score[i];

    auto dfs = [&ans, &score](auto &self, int cur, int sum, string name) -> void
    {
        if (cur == 5)
        {
            if(sum)
                ans.push_back({sum, name});
            return;
        }
        self(self, cur + 1, sum + score[cur], name + char('A' + cur));
        self(self, cur + 1, sum, name);
    };

    dfs(dfs, 0, 0, "");

    sort(ans.begin(), ans.end(), [](const pair<int, string> &x, const pair<int, string> &y) -> bool
         { return x.first > y.first || (x.first == y.first && x.second < y.second); });
    for(auto &[sum, name] : ans)
        cout << name << endl;
    return 0;
}

D - Repeated Sequence

题目大意

有一个周期为 \(N(1 \le N \le 2 \times 10 ^ 5)\) 的无限循环序列 \(A\),前 \(N\) 项为 \((A_1, A_2, ..., A_N)\),其中 \(1 \le A_i \le 10 ^ 9\)

判断在 \(A\) 中是否存在一个连续的子序列和等于 \(S(1 \le S \le 10 ^ {18})\)

解题思路

首先求出 \(M = \sum\limits_{i=1}^N\),因为是无限循环序列,所以答案一定是某一段区间和加上若干循环的部分。

例如,我们可以找出某一段 \((A_l, A_{l+1}, ..., A_r)\),这一段区间和等于 \(S \bmod M\),剩下的部分重复 \(\lfloor\frac{S}{M}\rfloor\) 次循环就好。因此问题等价于找出 \(A_1\)\(A_N\) 是否存在区间和等于 \(S \bmod M\)

但是还有一种情况会漏掉,例如 \(N = 4\)\(A_1A_2A_3\underline{A_4A_1A_2A_3A_4A_1}A_2A_3A_4\),其中下划线表示和等于 \(S\) 的字符串,这里实际实际要找的字符串是 \(A_4A_1\),这一段不是连续的子区间,而是一段后缀拼前缀的形式,因为无限循环,所以这个情况也要考虑到。

有两种处理办法:

  • \(A_1\)\(A_N\) 扩展成 \(A_1\)\(A_{2N}\),在里面找是否有区间和等于 \(S \bmod M\)
  • 注意到这是后缀拼前缀的形式,所以可以找出是否存在一个区间和等于 \(M - S \bmod M\),相当于在上面的例子中找出 \(A_2A_3\)\(A_2A_3\) 的区间和等于 \(M - S \bmod M\),所以剩下的部分就能通过后缀拼前缀的方式得到 \(S \bmod M\)

两种办法都是找某个区间和等于某个值,因为 \(A_i\) 都是正数,所以直接双指针就好,不过如果有负数的话,就要用哈希表了。

下面的代码是第一种做法。

参考代码
#include <iostream>
#include <vector>

using namespace std;
using LL = long long;

int main()
{
    int n;
    LL s;
    cin >> n >> s;
    vector<LL> pre(2 * n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> pre[i];
        pre[n + i] = pre[i];
        pre[i] += pre[i - 1];
    }
    for (int i = n + 1; i <= 2 * n; i++)
        pre[i] += pre[i - 1];
    s %= pre[n];
    for (int l = 0, r = 1; r <= 2 * n; r++)
    {
        while (pre[r] - pre[l] > s)
            l++;
        if (pre[r] - pre[l] == s)
        {
            cout << "Yes" << endl;
            return 0;
        }
    }
    cout << "No" << endl;
    return 0;
}

E - Takahashi is Slime 2

题目大意

有一个 \(H \times W(1 \le H, W \le 500)\) 的网格,每个网格 \((i, j)\) 都有一个史莱姆,强度为 \(S_{i,j}(1 \le S_{i, j} \le 10 ^ {12})\)

史莱姆王的初始位置是 \((P, Q)\),其他史莱姆和史莱姆王是敌对关系。史莱姆王想不断的吞并周围的史莱姆,设史莱姆王当前的强度为 \(A\) ,如果在他的身体周围上下左右 \(1\) 格有一个史莱姆的的强度是 \(B\),且 \(A > XB\),则史莱姆王可以吸收这个史莱姆,其中 \(X(1 \le X \le 10 ^ 9)\) 是一个给定的常数。当史莱姆王吸收了这个史莱姆之后,史莱姆王的强度将增加 \(B\),且被吸收的史莱姆所在的位置也会成为史莱姆王身体的一部分。史莱姆王身体的所有部分具有相同的强度。

问:史莱姆王最多可以达到多少强度?

解题思路

显然是优先吸收身体周围强度比较弱的史莱姆,用一个小顶堆维护所有身体周围的史莱姆,不断的向外扩展就好。

不过这道题要注意一下 \(X \le 10 ^ 9\)\(S_{i, j} \le 10 ^ {12}\),所以 \(X\times S_{i,j}\) 会爆 long long,用除法就好。

参考代码
#include <functional>
#include <iostream>
#include <queue>
#include <vector>

using namespace std;
using LL = long long;

const int dr[] = {-1, 1, 0, 0};
const int dc[] = {0, 0, -1, 1};

struct Node
{
    int r, c;
    LL s;
    bool operator>(const Node &other) const { return s > other.s; }
};

int main()
{
    int h, w, p, q;
    LL x;
    cin >> h >> w >> x >> p >> q;
    p--, q--;
    vector<vector<LL>> g(h, vector<LL>(w));
    for (int i = 0; i < h; i++)
        for (int j = 0; j < w; j++)
            cin >> g[i][j];

    auto in_lim = [h, w](int r, int c) -> bool
    { return r >= 0 && r < h && c >= 0 && c < w; };

    priority_queue<Node, vector<Node>, greater<Node>> heap;
    LL now = g[p][q];
    g[p][q] = -1;
    for (int i = 0; i < 4; i++)
    {
        int nr = p + dr[i], nc = q + dc[i];
        if (in_lim(nr, nc) && g[nr][nc] != -1)
        {
            heap.push({nr, nc, g[nr][nc]});
            g[nr][nc] = -1;
        }
    }
    while (!heap.empty())
    {
        auto [r, c, s] = heap.top();
        heap.pop();
        if (now / x < s || (now / x == s && now % x == 0))
            break;
        now += s;
        g[r][c] = -1;
        for (int i = 0; i < 4; i++)
        {
            int nr = r + dr[i], nc = c + dc[i];
            if (in_lim(nr, nc) && g[nr][nc] != -1)
            {
                heap.push({nr, nc, g[nr][nc]});
                g[nr][nc] = -1;
            }
        }
    }
    cout << now << endl;
    return 0;
}

F - Double Sum 2

题目大意

定义 \(f(x) = \left\{\begin{matrix}f(\frac{x}{2}), & \text{if } x \equiv 0 \bmod 2\\x, & \text{if } x \equiv 1 \bmod 2 \end{matrix}\right.\)

给你一个长度为 \(N(1 \le N \le 2 \times 10 ^ 5)\) 的序列 \(A = (A_1, A_2, ..., A_N)\),其中 \(1 \le A_i \le 10 ^ 7\) 求出 \(\sum\limits_{i=1}^N\sum\limits_{j=i}^Nf(A_i + A_j)\)

解题思路

可以发现,对于 \(f(a+b)\)

  • 如果 \(a\)\(b\) 一奇一偶,则 \(f(a+b) = a+b\)
  • 如果 \(a\)\(b\) 都是偶数,则 \(f(a+b) = \frac{a}{2}+\frac{b}{2}\)
  • 如果 \(a\)\(b\) 都是奇数,则 \(f(a + b) = \lfloor\frac{a}{2}\rfloor + \lfloor\frac{b}{2}\rfloor + 1\)

\(g[i] = \sum\limits_{j=1}^if(A_i + A_j)\),考虑怎么算出 \(g[i]\)\(g[i]\) 实际固定了 \(A_i\),因此还需要分类讨论:

  • 如果 \(A_i\) 是偶数,首先要加上前面所有的奇数。然后还剩下偶数的部分,根据刚刚的推论,偶数需要除以 \(2\),此时我们需要继续讨论 \(\frac{a}{2}\) 的奇偶性:如果 \(\frac{a}{2}\) 是偶数,就加上所有偶数且除以一次 \(2\) 之后是奇数的部分(相当于一次性加上多个 \(\frac{b}{2}\)),然后针对除以一次 \(2\) 之后是偶数的部分继续分类讨论......;如果 \(\frac{a}{2}\) 是奇数,转到 \(A_i\) 是奇数的讨论。

分析到这里可以发现,我们需要知道所有初始是奇数(二进制后缀是 \(1\))的和、所有初始是偶数且除以一次 \(2\) 之后是奇数(二进制后缀是 \(10\))的和,这可以通过一个 01Trie 树维护。

  • 如果 \(A_i\) 是奇数,首先要加上前面所有的偶数。然后还剩下奇数的部分,根据刚刚的推论,奇数实际是变成 \(\lfloor\frac{a}{2}\rfloor + \lfloor\frac{b}{2}\rfloor + 1\) ,此时我们需要继续讨论 \(\lfloor\frac{a}{2}\rfloor + 1\) 的奇偶性:如果 \(\lfloor\frac{a}{2}\rfloor + 1\) 是奇数,就加上所有奇数且除以一次 \(2\) 之后是偶数的部分(相当于一次性加上多个 \(\lfloor\frac{b}{2}\rfloor\)),然后针对除以一次 \(2\) 之后是奇数的部分继续分类讨论......;如果 \(\lfloor\frac{a}{2}\rfloor + 1\) 是偶数,转到 \(A_i\) 是偶数的讨论。

因为 \(1 \le A_i \le 10 ^ 7\),所以上述递归最多发生 \(\lceil \log_2(\max(A_i))\rceil\) 次,因为超出这个范围之后都是 \(0\),就没必要走下去了。

实现的时候要注意一下,实际要让 \(a \leftarrow \frac{a+1}{2}\),这样可以同时处理奇数和偶数的情况。

参考代码
#include <cmath>
#include <iostream>

using namespace std;
using LL = long long;

const int MAXA = 1E7;
const int LEN = ceil(log2(MAXA));

struct Trie
{
    struct Node
    {
        Node *ne[2];
        LL sum;
        int cnt;

        Node *at_or_new(int ch) { return ne[ch] == nullptr ? ne[ch] = new Node() : ne[ch]; }

        Node *at(int ch) const { return ne[ch]; }
    };

    Node *root = new Node();

    void insert(LL x)
    {
        Node *p = root;
        for (int i = 0; i <= LEN; i++)
        {
            int ch = x & 1;
            p = p->at_or_new(ch);
            p->sum += x;
            p->cnt++;
            x >>= 1;
        }
    }

    LL search(LL x) const
    {
        Node *p = root;
        LL ans = 0;
        while (p != nullptr)
        {
            int ch = x & 1;
            Node *p_xor = p->at(ch ^ 1);
            if(p_xor != nullptr)
                ans += p_xor->cnt * x + p_xor->sum;
            p = p->at(ch);
            x = (x + 1) >> 1;
        }
        return ans;
    }
};

int main()
{
    int n;
    cin >> n;
    Trie t;
    LL ans = 0;
    while(n--)
    {
        LL a;
        cin >> a;
        t.insert(a);
        ans += t.search(a);
    }
    cout << ans << endl;
    return 0;
}

G - Abs Sum

题目大意

给你两个长度为 \(N(1 \le N \le 10 ^ 5)\) 的序列 \(A = (A_1, A_2, ..., A_N)\)\(B = (B_1, B_2, ..., B_N)\),其中 \(0 \le A_i, B_j \le 2 \times 10 ^ 8\)

给你 \(Q(1 \le Q \le 10 ^ 4)\) 个询问,第 \(k\) 个询问给你两个数字 \(X_k(1 \le X_k \le N)\)\(Y_k(1 \le Y_k \le N)\),求出 \(\sum\limits_{i=1}^{X_k}\sum\limits_{j=1}^{Y_k}|A_i - B_j|\)

解题思路

我们可以构造一个 \(N \times N\) 的矩阵,矩阵的第 \(i\) 行第 \(j\) 列表示 \(|A_i-B_j|\) 的值,一个询问 \((X_k, Y_k)\) 相当于求以 \((1, 1)\) 为左上角,\((X_k, Y_k)\) 为右下角的矩阵和。

假设我们已经有以 \((X, Y)\) 为右下角的矩阵和,考虑如何扩展到 \((X+1, Y)\),这样会增加一行,也就是增加 \(\sum\limits_{j=1}^Y|A_{X+1} - B_j|\) 的值,显然要拆成 \(A_{X+1} \ge B_j\)\(A_{X+1} < B_j\) 两个部分,这可以用树状数组维护 \(B\) 中每个数字出现的次数,以及前缀和,同理,还需要一个树状数组维护 \(A\) 的相关信息。考虑到 \(0 \le A_i, B_j \le 2 \times 10 ^ 8\),需要离散化,这样树状数组的下标对应的是数字的排名。

每一次扩展或者收缩矩阵需要 \(\log N\) 的时间,为了减少扩展的次数,将所有的查询按照莫队排序即可,取块长度 \(S = \frac{N}{\sqrt Q}\) 即可。

参考代码
#include <algorithm>
#include <cmath>
#include <iostream>
#include <utility>
#include <vector>

using namespace std;
using LL = long long;

struct FenwickTree
{
    int n;
    vector<int> cnt;
    vector<LL> sum;

    FenwickTree(int n) : n(n), cnt(n + 1), sum(n + 1) {}

    int lowbit(int x) const { return x & -x; }

    pair<int, LL> prefix(int x) const
    {
        int c = 0;
        LL s = 0;
        for (; x; x -= lowbit(x))
        {
            c += cnt[x];
            s += sum[x];
        }
        return {c, s};
    }

    pair<int, LL> query(int l, int r) const
    {
        auto [cr, sr] = prefix(r);
        auto [cl, sl] = prefix(l - 1);
        return {cr - cl, sr - sl};
    }

    void add(int x, LL num)
    {
        for (; x <= n; x += lowbit(x))
        {
            cnt[x]++;
            sum[x] += num;
        }
    }

    void sub(int x, LL num)
    {
        for (; x <= n; x += lowbit(x))
        {
            cnt[x]--;
            sum[x] -= num;
        }
    }
};

struct Query
{
    int x, y, b, id;
    bool operator<(const Query &other) const { return b != other.b ? b < other.b : (b % 2 == 0 ? y < other.y : y > other.y); }
};

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<LL> a(n), b(n);
    for (auto &x : a)
        cin >> x;
    for (auto &x : b)
        cin >> x;
    vector<LL> val = a;
    val.insert(val.end(), b.begin(), b.end());
    sort(val.begin(), val.end());

    val.erase(unique(val.begin(), val.end()), val.end());
    int len = val.size() + 1;
    for (auto &x : a)
        x = lower_bound(val.begin(), val.end(), x) - val.begin() + 1;
    for (auto &x : b)
        x = lower_bound(val.begin(), val.end(), x) - val.begin() + 1;
    FenwickTree ta(len), tb(len);
    int k;
    cin >> k;
    int block_size = max(1, (int)(n / sqrt(k)));
    vector<Query> q(k);
    for (int i = 0; i < k; i++)
    {
        auto &[x, y, b, id] = q[i];
        cin >> x >> y;
        x--, y--;
        b = x / block_size;
        id = i;
    }
    sort(q.begin(), q.end());
    LL sum = 0, sum_a = 0, sum_b = 0;
    int x = -1, y = -1;

    auto inc_x = [&]() -> void
    {
        int rk = a[++x];
        LL num = val[rk - 1];
        auto [left_cnt, left_sum] = tb.query(1, rk);
        sum += left_cnt * num - left_sum;
        sum += (sum_b - left_sum) - num * (y + 1 - left_cnt);
        ta.add(rk, num);
        sum_a += num;
    };

    auto dec_x = [&]() -> void
    {
        int rk = a[x--];
        LL num = val[rk - 1];
        auto [left_cnt, left_sum] = tb.query(1, rk);
        sum -= left_cnt * num - left_sum;
        sum -= (sum_b - left_sum) - num * (y + 1 - left_cnt);
        ta.sub(rk, num);
        sum_a -= num;
    };

    auto inc_y = [&]() -> void
    {
        int rk = b[++y];
        LL num = val[rk - 1];
        auto [left_cnt, left_sum] = ta.query(1, rk);
        sum += left_cnt * num - left_sum;
        sum += (sum_a - left_sum) - num * (x + 1 - left_cnt);
        tb.add(rk, num);
        sum_b += num;
    };

    auto dec_y = [&]() -> void
    {
        int rk = b[y--];
        LL num = val[rk - 1];
        auto [left_cnt, left_sum] = ta.query(1, rk);
        sum -= left_cnt * num - left_sum;
        sum -= (sum_a - left_sum) - num * (x + 1 - left_cnt);
        tb.sub(rk, num);
        sum_b -= num;
    };

    vector<LL> ans(k);
    for (auto [qx, qy, _, id] : q)
    {
        while (x < qx)
            inc_x();
        while (y < qy)
            inc_y();
        while (x > qx)
            dec_x();
        while (y > qy)
            dec_y();
        ans[id] = sum;
    }
    for (auto x : ans)
        cout << x << '\n';
    return 0;
}


最后更新: 2025-01-02
创建日期: 2025-01-02