AtCoder Beginner Contest 384

省流版
  • A. 模拟转换即可
  • B. 模拟判断即可
  • C. 枚举所有情况排序即可
  • D. 分区间和与跨区间和,后者为一个前后缀+区间和,分别判断即可
  • E. 贪心选强度最小的粘液,优先队列维护即可
  • F. 考虑除以\(2^k\),枚举\(k\),统计满足\(A_i + A_j = 0 \mod 2^k\)\(A_i + A_j \neq 0 \mod 2^{k+1}\)\(A_i + B_i\)的和,即做两次D题的做法即可。

A - aaaadaa (abc384 A)

题目大意

给定一个字符串\(S\)和两个字符\(c_1\)\(c_2\),将所有非 \(c_1\) 替换为 \(c_2\)

解题思路

依次枚举每个字符判断即可。

神奇的代码
_, a, b = input().strip().split()
c = input().strip()
d = ''.join([b if x != a else x for x in c])
print(d)



B - ARC Division (abc384 B)

题目大意

打比赛,根据当前rating和表现分a更新rating

两类比赛:

  • Div1: rating在1600-2799之间,rating增加a
  • Div2: rating在1200-2399之间,rating增加a

如果rating不在范围内,不会增加rating

给了n场比赛的ratinga,求最终rating

解题思路

按照题意模拟即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, r;
    cin >> n >> r;
    while (n--) {
        int d, a;
        cin >> d >> a;
        if (d == 1 && 1600 <= r && r <= 2799) {
            r += a;
        } else if (d == 2 && 1200 <= r && r <= 2399) {
            r += a;
        }
    }
    cout << r << '\n';

    return 0;
}



C - Perfect Standings (abc384 C)

题目大意

给定五道题的分数,对于一个字符串,其分数为字符对应题目的分数之和。

共有 \(2^5 - 1=31\) 种情况,按照分数从高到低输出,如果分数相同,按照字典序输出。

解题思路

DFS或迭代的方法枚举所有情况,然后排序输出即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    array<int, 5> a;
    for (auto& x : a)
        cin >> x;
    vector<string> t1{""};
    for (int i = 0; i < 5; ++i) {
        vector<string> t2 = t1;
        for (auto& x : t1) {
            auto y = x + char('A' + i);
            t2.push_back(y);
        }
        t2.swap(t1);
    }
    auto score = [&](const string& x) {
        int res = 0;
        for (auto c : x) {
            res += a[c - 'A'];
        }
        return res;
    };
    sort(t1.begin(), t1.end(), [&](const string& x, const string& y) {
        int sx = score(x), sy = score(y);
        if (sx != sy)
            return sx > sy;
        return x < y;
    });
    t1.pop_back();
    for (auto& x : t1)
        cout << x << '\n';
    return 0;
}



D - Repeated Sequence (abc384 D)

题目大意

给定一个 \(N\) 周期序列,其中前 \(N\) 项为 \(A=(A _ 1,A _ 2,A _ 3,\dotsc)\)

判断是否存在一个和为 \(S\) 的非空连续子序列。

解题思路

所有数都是正数。

这个非空序列只有两种情况:

  • 在一个周期内,很显然就是一个区间和的问题,转换成两个前缀和的差,用set存储前缀和判断是否存在即可。
  • 跨越多个周期,此时就是一个后缀+一个前缀和若干个区间和的问题,枚举后缀,计算区间和数量,再判断对应前缀是否存在即可。
神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    LL s;
    cin >> n >> s;
    vector<LL> a(n);
    for (auto& x : a)
        cin >> x;
    vector<LL> sum(a);
    partial_sum(a.begin(), a.end(), sum.begin());
    set<LL> st{0};
    bool ok = false;
    for (auto x : sum) {
        if (st.count(x - s)) {
            ok = true;
            break;
        }
        st.insert(x);
    }
    if (!ok) {
        reverse(a.begin(), a.end());
        LL tot = accumulate(a.begin(), a.end(), 0ll);
        LL sufsum = 0;
        for (auto& i : a) {
            sufsum += i;
            LL left = (s - sufsum) % tot;
            if (st.count(left)) {
                ok = true;
                break;
            }
        }
    }

    if (ok)
        cout << "Yes" << '\n';
    else
        cout << "No" << '\n';

    return 0;
}



E - Takahashi is Slime 2 (abc384 E)

题目大意

给定一个 \(H\times W\) 的网格,每个单元格中有一个整数 \(S _ {i,j}\),以及一个整数 \(X\) 和初始位置 \(P,Q\)

初始时,高桥位于 \((P,Q)\) 单元格中,强度为 \(S _ {P,Q}\)

现在高桥可以上下左右移动,每次移动后,高桥会吸收与其相邻的粘液中强度严格小于高桥强度的 \(\dfrac{1}{X}\) 倍的粘液,并将其吸收,高桥的强度会增加被吸收粘液的强度。

问高桥最终的强度是多少。

解题思路

很显然,我们肯定选择强度最小的粘液吸收,由于粘液会随着高桥的移动越来越多,可以用一个优先队列来维护当前可接触到的粘液的最小值,然后贪心选粘液强度最小值即可。

注意\(S_{i,j} X\)会超出long long范围,需要用__int128来存储。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int h, w, x, p, q;
    cin >> h >> w >> x >> p >> q;
    vector<vector<LL>> a(h, vector<LL>(w));
    for (auto& i : a)
        for (auto& j : i)
            cin >> j;
    priority_queue<array<LL, 3>, vector<array<LL, 3>>, greater<array<LL, 3>>>
        pq;
    array<int, 4> dx = {1, 0, -1, 0};
    array<int, 4> dy = {0, 1, 0, -1};
    --p, --q;
    for (int i = 0; i < 4; i++) {
        int nx = p + dx[i];
        int ny = q + dy[i];
        if (nx < 0 || nx >= h || ny < 0 || ny >= w)
            continue;
        pq.push({a[nx][ny], nx, ny});
        a[nx][ny] = -1;
    }
    LL cur = a[p][q];
    a[p][q] = -1;
    while (!pq.empty()) {
        auto [s, u, v] = pq.top();
        if (__int128(s) * x >= cur) {
            break;
        }
        pq.pop();
        cur += s;
        for (int i = 0; i < 4; i++) {
            int nx = u + dx[i];
            int ny = v + dy[i];
            if (nx < 0 || nx >= h || ny < 0 || ny >= w || a[nx][ny] == -1)
                continue;
            pq.push({a[nx][ny], nx, ny});
            a[nx][ny] = -1;
        }
    }
    cout << cur << '\n';

    return 0;
}



F - Double Sum 2 (abc384 F)

题目大意

定义 \(f(x)\) 表示将 \(2\) 的幂因子全部去掉后的数。

给定长度为 \(N\) 的整数序列 \(A=(A_1,A_2,\ldots,A_N)\) ,求 \(\displaystyle \sum_{i=1}^N \sum_{j=i}^N f(A_i+A_j)\)

解题思路

建议看第二个做法,该做法是赛时的做法,分情况讨论复杂了,写题解时发现第二类情况的做法具有通用型。

首先我们可以将 \(A_i\) 分解为 \(2^k \times t\) ,其中 \(t\) 为奇数,然后对\(k\)进行排序。

然后考虑两个数\(A_i, A_j(i < j)\),根据其 \(k\) 是否相同,可以分为两种情况:

  • \(k_i \neq k_j\),即 \(k_i < k_j\),此时 \(f(A_i+A_j)=f(2^{k_i}t_i+2^{k_j}t_j)=f(2^{k_i}(t_i + 2^{k_j - k_i}t_j)) = t_i + 2^{k_j - k_i}t_j = \frac{A_i + A_j}{2^{k_i}}\),因为奇+偶=奇。所以我们可以枚举 \(i\),考虑所有的 \(j\)的贡献,设其数量为 \(cnt\) ,对应的 \(A_j\)的和为 \(sum\) ,那么 \(\sum_j f(A_i+A_j) = \frac{sum + cnt \times A_i}{2^{k_i}}\)
  • \(k_i = k_j\),此时 \(f(A_i+A_j)=f(2^{k_i}t_i+2^{k_j}t_j)=f(2^{k_i}(t_i + t_j)) = f(t_i + t_j)\),由于奇+奇=偶,所以\(t_i + t_j\)仍要除以\(2\)的幂,但关键是幂是多少呢?诚然,我们可以直接加然后求幂,但这样复杂度必然会变成\(O(n^2)\)。不能这样做。

注意到\(A_i \leq 1e7\),因此幂的可能情况不超过\(25\)种,我们可以枚举幂,然后统计每个幂\(k\),有多少的\(A_i + A_j\)\(2^k\)的倍数。注意这个的统计和\(D题\)做法一样:如果 \(A_i + A_j\)\(2^k\) 的倍数,就意味着 \(A_i \% 2^k + A_j \% 2^k = 0 \mod 2^k\),即 \(A_i \% 2^k = 2^k - A_j \% 2^k\),我们可以枚举\(A_i\),用set统计符合条件的\(A_j\)的数量即可,通过维护对应取模值为\(x\)\(A_i\)的和及数量,就能得到满足\(A_i + A_j = 0 \mod 2^k\)\(\sum\sum A_i + A_j\),该幂其对答案的贡献就是\(\frac{\sum\sum A_i + A_j}{2^k}\)

但这并不是正确的,考虑\(\frac{A_i + A_j}{2^k}\)才是奇数,再上述计数下,最终的和包括了 \(\frac{A_i + A_j}{2^k} + \frac{A_i + A_j}{2^{k-1}} + \cdots + \frac{A_i + A_j}{2^1}\),实际上只有\(\frac{A_i + A_j}{2^k}\)对答案才有贡献(它才是\(f(A_i + A_j)\)的值),其余都不是,我们得剔除其余部分。

怎么剔除呢?注意到是等比数列,设\(S_k = \sum_i\sum_j \frac{A_i + A_j}{2^k} + \frac{A_i + A_j}{2^{k-1}} + \cdots + \frac{A_i + A_j}{2^1}\),则\(2S = \sum_i\sum_j\frac{A_i + A_j}{2^k-1} + \frac{A_i + A_j}{2^{k-1}} + \cdots + A_i + A_j\),则\(2S_k - S_k = S_k = \sum_i\sum_j A_i + A_j - \frac{A_i + A_j}{2^k-1}\)

所以\(\frac{A_i + A_j}{2^k-1} = \sum_i \sum_j A_i + A_j - S_k\),左右两边对 \(k\)求和,就是\(ans = \sum_i\sum_j A_i + A_j - \sum_k S_k\)。第一项就是所有\(A_i\)的俩俩相加,第二项就是我们通过上述方法求得和。这样就得到了答案。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    vector<int> a(n);
    for (auto& i : a)
        cin >> i;
    vector<int> c2(n);
    auto calc = [](int x) {
        int ret = 0;
        while (~x & 1) {
            x >>= 1;
            ++ret;
        }
        return ret;
    };
    int up = 25;
    vector<int> p2(up);
    p2[0] = 1;
    for (auto i = 1; i < up; ++i) {
        p2[i] = p2[i - 1] << 1;
    }
    LL ans = 0;
    for (auto i = 0; i < n; ++i) {
        c2[i] = calc(a[i]);
        ans += a[i] / p2[c2[i]];
    }
    vector<int> id(n);
    iota(id.begin(), id.end(), 0);
    sort(id.begin(), id.end(), [&](int x, int y) { return c2[x] < c2[y]; });
    LL tot = accumulate(a.begin(), a.end(), 0ll);
    int cnt = n;
    auto solve = [&](auto l, auto r) {
        LL ret = 0;
        for (int i = up - 1; i >= 1; --i) {
            map<int, LL> sum, cnt;
            for (auto j = l; j != r; j = next(j)) {
                int mod = a[*j] % p2[i];
                int other = mod ? (p2[i] - mod) : 0;
                ret += (sum[other] + 1ll * cnt[other] * a[*j]) / p2[i];
                sum[mod] += a[*j];
                cnt[mod] += 1;
            }
        }
        LL all = 0;
        LL pre = 0;
        for (auto j = l; j != r; j = next(j)) {
            all += pre + 1ll * a[*j] * (j - l);
            pre += a[*j];
        }
        ret = all - ret;
        return ret;
    };
    for (auto i = id.begin(); i != id.end();) {
        LL ss = a[*i];
        LL cnt2 = 1;
        tot -= a[*i];
        cnt -= 1;
        auto nxt = next(i);
        while (nxt != id.end() && c2[*nxt] == c2[*i]) {
            tot -= a[*nxt];
            cnt -= 1;
            ss += a[*nxt];
            cnt2 += 1;
            nxt = next(nxt);
        }
        ans += 1ll * cnt2 * tot / p2[c2[*i]] + 1ll * cnt * (ss) / p2[c2[*i]];
        ans += solve(i, nxt);
        i = nxt;
    }
    cout << ans << '\n';

    return 0;
}



写到这里忽然发现这种做法也能覆盖\(k_i \neq k_j\)的情况。即\(\sum_i\sum_j f(A_i + A_j) = \sum_i\sum_j\frac{A_i + A_j}{2^k}\),直接枚举\(i,j\)必然是 \(O(n^2)\),所以我们转而枚举\(k\),即\(\sum_k \frac{1}{2^k} \sum_i\sum_j{A_i + A_j}[A_i + A_j = 0 \mod 2^k, A_i + A_j \neq 0 \mod 2^{k+1}]\),然后设法统计这样的\(A_i + A_j\)的和,对所有的\(k\)相加,即为答案。

怎么统计呢?统计\(A_i + A_j = 0 \mod 2^k\)\(A_i + A_j\)和的方法形同D题做法,即枚举\(A_i\),然后用map维护其对应取模的值和数量,但上述要求不包含\(0 \mod 2^{k+1}\)的情况,那就直接从\(0 \mod 2^k\)减去\(0 \mod 2^{k+1}\)即可,这样就能得到\(0 \mod 2^k\)但不是\(0 \mod 2^{k+1}\)的情况。

得用unordered_map来维护,map可能会超时。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    vector<int> a(n);
    for (auto& i : a)
        cin >> i;
    constexpr int up = 26;
    vector<int> p2(up);
    p2[0] = 1;
    for (auto i = 1; i < up; ++i) {
        p2[i] = p2[i - 1] << 1;
    }
    LL ans = 0;
    array<unordered_map<int, LL>, up> sum, cnt;
    for (auto j = 0; j < n; j++) {
        for (int i = up - 2; i >= 0; --i) {
            int mod = a[j] % p2[i];
            int other = mod ? (p2[i] - mod) : 0;
            int mod2 = a[j] % p2[i + 1];
            int other2 = mod2 ? (p2[i + 1] - mod2) : 0;

            sum[i][mod] += a[j];
            cnt[i][mod] += 1;
            ans += (sum[i][other] - sum[i + 1][other2] +
                    (cnt[i][other] - cnt[i + 1][other2]) * a[j]) /
                   p2[i];
        }
    }
    cout << ans << '\n';

    return 0;
}


G - Abs Sum (abc384 G)

题目大意

给定长度为 \(N\) 的整数序列 \(A=(A_1,A_2,\ldots,A_N)\)\(B=(B_1,B_2,\ldots,B_N)\) 以及长度为 \(K\) 的整数序列 \(X=(X_1,X_2,\ldots,X_K)\)\(Y=(Y_1,Y_2,\ldots,Y_K)\)

求每个 \(k=1,2,\ldots,K\) 的长度 \(\displaystyle \sum_{i=1}^{X_k} \sum_{j=1}^{Y_k} |A_i-B_j|\)

解题思路

<++>

神奇的代码



posted @ 2024-12-14 23:18  ~Lanly~  阅读(144)  评论(0编辑  收藏  举报