[ABC313] C~E + G 题解

[ABC313] C~E 题解

C - Approximate Equalization 2

让所有的数字都尽量接近平均数,先算出平均数,然后把所有数字分成两份,一份要加,一份要减,因为平均数有余数,余数肯定给最大的几个,所以这样计算总共需要加减多少个,然后在加减里面取 \(\max\) 即可。

时间复杂度:\(O(n\log n)\)

#include <algorithm>
#include <iostream>
#define int long long
using namespace std;
const int N = 2e5 + 10;

int n, a[N];
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n;
    int s = 0, r;
    for(int i = 1; i <= n; i ++)
        cin >> a[i], s += a[i];
    r = s % n, s /= n;
    sort(a + 1, a + n + 1);
    int cnt1 = 0, cnt2 = 0;
    for(int i = 1; i <= n - r; i ++) {
        if(a[i] <= s)
            cnt1 += s - a[i];
        else cnt2 += a[i] - s;
    }
    for(int i = n - r + 1; i <= n; i ++) {
        if(a[i] <= s + 1)
            cnt1 += s - a[i];
        else cnt2 += a[i] - s - 1;
    }
    cout << max(cnt1, cnt2) << '\n';
    return 0;
}

D - Odd or Even

设前 \(k - 1\) 个数的和的奇偶性为 \(x\),则使用 \(n - k + 1\) 次询问得到 \(A_{k\sim n}\) 异或 \(x\) 的奇偶性。

考虑得到 \(A_{1\sim k - 1}\) 异或 \(x\) 的奇偶性,考虑用别的位置的贡献消除 \(A_i\) 的贡献,由于 \(k < n\) 所以 \(A_{n - 1}\) 的贡献已经被算过了,不妨用 \(A_{n - 1}\) 消除贡献,可以考虑如下构造:a[n - 1] xor a[n] xor queryquery 查询 \(\{j\in{[1, k -1]\wedge j\ne i}\vee j = [n - 1, n]\}\),这样就可以得到 \(A_i \oplus x\) 的奇偶性,由于 \(k\) 是奇数,所以前 \(k\) 个数的奇偶异或 \(x\) 的异或和等于原值的异或和异或 \(x\),也就是 \(A_k\),将 \(A_k \oplus x\)\(A_k\) 比较可得出 \(x\) 的奇偶性,然后全部数字异或 \(x\) 消除它的影响就是原数组了。

时间复杂度:\(O(nk)\)

// Problem: D - Odd or Even
// Contest: AtCoder - AtCoder Beginner Contest 313
// Author: Moyou
// Copyright (c) 2023 Moyou All rights reserved.
// Date: 2023-08-05 20:27:06

#include <algorithm>
#include <cstring>
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
const int N = 2e5 + 10;

int n, k;
bool a[N];
int t[N], tmp[N], x;
int ask() {
    memcpy(tmp, t, sizeof t);
    sort(tmp + 1, tmp + k + 1);
    cout << "? ";
    for(int i = 1; i <= k; i ++) cout << tmp[i] << ' ';
    int x;
    cout << '\n' << flush;
    cin >> x;
    return x;
}

signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n >> k;
    iota(t + 1, t + k + 1, 1);
    for(int i = k; i <= n; i ++) {
        t[k] = i;
        a[i] = ask();
    }
    for(int i = 1; i < k; i ++) {
        t[i] = n - 1;
        a[i] = a[n] ^ ask() ^ a[n - 1];
        t[i] = i;
    }
    int sum = 0;
    for(int i = 1; i <= k; i ++) sum += a[i];
    sum &= 1;
    sum ^= a[k]; 
    for(int i = 1; i <= n; i ++)
        a[i] ^= sum;
    cout << "! ";
    for(int i = 1; i <= n; i ++) cout << a[i] << ' ';
    cout << '\n' << flush;

    return 0;
}

E - Duplicate

首先观察到如果有连续两个数字大于 \(2\),则会无限循环,这样会让其中一个数字无限复制。

所以假设第 \(i\) 个位置之后的都已经被消除了,使用了 \(step\) 步,现在要消除第 \(i\) 个位置的字符,则它身后的字符会复制 \((step + 1)(s_i - 1)\) 次,这样迭代更新 \(step\) 直到只剩一个字符为止。

时间复杂度:\(O(n)\)

int step = 0;
for(int i = n - 1; i; i --) {
    if(s[i] >= '2' && s[i - 1] >= '2') return cout << -1 << '\n', 0;
    step ++, step %= mod, step = ((s[i] - '0' - 1) * step % mod + step) % mod;
}

G - Redistribution of Piles

在一轮操作内,一定是先做连续的一段 A 操作,再做连续的一段 B 操作。

因为如果先做了 B 操作之后突然做 A 操作相当于这两次什么都没干,反之亦然。

所以暴力的思路就是枚举做了多少次 A 操作,然后可以计算出最多做多少次 B 操作。

这样是 \(O(V + n\log n)\) 的,显然会超时。

考虑化简式子。

\[\begin{eqnarray} &\sum_{j=a_{i - 1} + 1}^{a_i}(\lfloor \dfrac{(j - a_{i - 1})(n - i + 1) + sum}{n}\rfloor + 1)\\ =&a_{i} - a_{i - 1}+\sum_{j=a_{i - 1} + 1}^{a_i}(\lfloor \dfrac{(j - a_{i - 1})(n - i + 1) + sum}{n}\rfloor)\\ =&a_{i} - a_{i - 1}+\sum_{j=a_{i - 1} + 1}^{a_i}\lfloor \dfrac{(n - i + 1)j - (n - i + 1)a_{i - 1} + sum}{n}\rfloor\\ \end{eqnarray} \]

最后一行是类欧的经典形式 \(\sum \lfloor \dfrac{ai+b}c \rfloor\),然后这题就做完了,时间复杂度:\(O(n\log V)\)

由于博主太菜不会类欧,所以使用了 atcoder 库下的 floor_sum

// Problem: G - Redistribution of Piles
// Contest: AtCoder - AtCoder Beginner Contest 313
// URL: https://atcoder.jp/contests/abc313/tasks/abc313_g
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// Author: Moyou
// Copyright (c) 2023 Moyou All rights reserved.
// Date: 2023-08-07 18:09:55

#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <set>
#include "atcoder/math.hpp"
#include "atcoder/modint.hpp"
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5 + 10;
using mint = atcoder::modint998244353;
using ll = long long;
ll n, a[N];

signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n;
    for(int i = 1; i <= n; i ++)
        cin >> a[i];
    sort(a + 1, a + n + 1);
    mint ans = a[1] + 1;
    ll sum = n * a[1];
    for(int i = 2; i <= n; i ++) {
        ll k = n - i + 1;
        ans += atcoder::floor_sum(a[i] + 1, n, k, -k * a[i - 1] + sum) - atcoder::floor_sum(a[i - 1] + 1, n, k, -k * a[i - 1] + sum); // atcoder 库的类欧是 0~n-1 的,所以要 +1
        sum += (a[i] - a[i - 1]) * (n - i + 1);
        ans += a[i] - a[i - 1];
    }
    cout << ans.val() << '\n';
    
    return 0;
}
posted @ 2023-08-07 12:29  MoyouSayuki  阅读(38)  评论(0编辑  收藏  举报
:name :name