Atcoder Beginner Contest 440

比赛链接:ABC440

A - Octave

输出 \(X \times 2^Y\)

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

#include <bits/stdc++.h>
using namespace std;

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int x, a;
    cin >> x >> a;
    cout << x * (1 << a) << endl;
    return 0;
}

B - Trifect

排序后输出前三位数字对应原序列中的下标。

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

#include <bits/stdc++.h>
using namespace std;

const int N = 35;
int n, a[N], p[N];

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i], p[i] = i;
    sort(p + 1, p + n + 1, [&](int x, int y){
        return a[x] < a[y];
    });
    cout << p[1] << " " << p[2] << " " << p[3] << endl;
    return 0;
}

C - Striped Horse

题目答案存在周期性,且周期为 \(2W\),用一个桶维护所有 \(i\ \%\ 2W\)\(C_i\) 的和。题目要求的答案等价于桶中连续长度为 \(W\) 的区间和的最小值或者是桶中开头结尾两部分拼接起来的长度为 \(W\) 的区间和的最小值。对于 \(i + x\) 的操作,实际上只是给桶中的值加上了一个偏移量,对答案结果并没有任何影响。

时间复杂度: \(O(N + W)\)

#include <bits/stdc++.h>
using namespace std;

#define int long long

const int N = 4e5 + 5;
int n, w, c[N], sum[N], pre[N];

void solve()
{
    cin >> n >> w;
    for (int i = 0; i <= 2 * w; i++)
        sum[i] = pre[i] = 0;
    for (int i = 1; i <= n; i++)
    {
        cin >> c[i];
        sum[i % (2 * w) + 1] += c[i];
    }
    for (int i = 1; i <= 2 * w; i++)
        pre[i] = pre[i - 1] + sum[i];
    int ans = LLONG_MAX;
    for (int l = 1; l <= 2 * w; l++)
    {
        int r = l + w - 1;
        if (r > 2 * w)
            r -= 2 * w;
        if (l > r)
            ans = min(ans, pre[2 * w] - pre[l - 1] + pre[r]);
        else
            ans = min(ans, pre[r] - pre[l - 1]);
    }
    cout << ans << "\n";
}

signed main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T;
    cin >> T;
    while (T--)
        solve();
    return 0;
}

D - Forbidden List 2

考虑对答案进行二分。

对于每一次询问,假设当前二分的值为 \(mid\),我们可以利用二分统计出原序列中小于等于 \(X_i\) 的值的数量 \(cnt_{X_i}\),和小于等于 \(mid\) 的值的数量 \(cnt_{mid}\)

  • 如果 \(cnt_{X_i} - cnt_{mid} > K_i\),说明在前 \(X_i\) 个数中,有超过 \(K_i\) 个数小于等于 \(mid\),所以答案一定小于等于 \(mid\),更新右边界
  • 否则,说明在前 \(X_i\) 个数中,有不超过 \(K_i\) 个数小于等于 \(mid\),所以答案一定大于 \(mid\),更新左边界。

时间复杂度: \(O(Q \log N \log \max A_i)\)

#include <bits/stdc++.h>
using namespace std;

#define int long long 

const int N = 3e5 + 5;
int n, q, a[N];

signed main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n >> q;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    sort(a + 1, a + n + 1);
    while (q--)
    {
        int x, y;
        cin >> x >> y;
        int pos = lower_bound(a + 1, a + n + 1, x) - a - 1;
        int l = x, r = INT_MAX;
        while (l + 1 < r)
        {
            int mid = (l + r) >> 1;
            int cnt = lower_bound(a + 1, a + n + 1, mid) - a - 1;
            if (mid - (cnt - pos) - x + 1 > y)
                r = mid;
            else
                l = mid;
        }
        cout << l << "\n";
    }
    return 0;
}

E - Cookies

题目要求前 \(X\) 大的美味值总和,我们可以先把最大的美味值总和 \(MaxSum = K \times A_{max}\) 求出来,然后求出前 \(X - 1\) 小的将其中部分饼干进行替换的损失。

通过 BFS 找出前 \(X - 1\) 小的损失:

  • 维护一个三元组 \((lstid, cnt, loss)\),分别表示上一次替换的饼干编号、已经替换的饼干数量和当前的损失值。
  • 构造出一个损失数组 \(del_i = A_{max} - A_{i}\),并对损失数组从大到小排序。
  • 当我们从队列中取出一个三元组的时候我们可以有两种方式进行下一步操作:
    • 将上次替换掉的饼干换成损失更大的饼干,得到三元组 \((lstid + 1, cnt, loss - del_{lstid} + del_{lstid + 1})\)
    • 在当前基础上,再多替换一个同类饼干,得到三元组 \((lstid, cnt + 1, loss + del_{lstid})\)
  • 用优先队列对三元组进行维护(按照损失大小排序),可以保证最多只会从队列中去出 \(X - 1\) 个三元组,从而求出前 \(X - 1\) 小的损失。
  • 答案为 \(MaxSum - loss\)

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

#include <bits/stdc++.h>
using namespace std;

#define int long long

const int N = 55;
int n, k, x, a[N];
struct node
{
    int lstid, cnt, loss;
    bool operator< (const node &x) const
    {
        return loss > x.loss;
    }
};

signed main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n >> k >> x;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    sort(a + 1, a + n + 1);
    int maxm = a[n] * k;
    vector<int> del;
    for (int i = 1; i < n; i++)
        del.push_back(a[n] - a[i]);
    sort(del.begin(), del.end());

    if (n == 1)
    {
        for (int i = 1; i <= x; i++)
            cout << maxm << "\n";
        return 0;
    }

    cout << maxm << "\n";
    x--;

    priority_queue<node> pq;
    pq.push((node){0, 1, del[0]});
    while (x-- && !pq.empty())
    {
        node cur = pq.top();
        pq.pop();
        cout << maxm - cur.loss << "\n";
        if (cur.lstid + 1 < (int)del.size())
            pq.push((node){cur.lstid + 1, cur.cnt, cur.loss - del[cur.lstid] + del[cur.lstid + 1]});
        if (cur.cnt < k)
            pq.push((node){cur.lstid, cur.cnt + 1, cur.loss + del[cur.lstid]});
    }
    return 0;
}

F - Egoism

贪心的考虑,我们需要尽量让最大的 \(A_i\) 都排在 \(B_i = 2\) 后面。记录两个集合 \(S_1\)\(S_2\),分别表示 \(B_i = 1\)\(B_i = 2\) 的位置对应的 \(A_i\) 的值。

集合 \(S_1\) 为空

  • 所有马的 \(B_i = 2\),要让 \(A_i\) 最小的马排在第一位,答案为 \(2\sum_{i=1}^N A_i - \min_{1 \leq i \leq N} A_i\)

集合 \(S_2\) 为空

  • 所有马都没有翻倍机会,答案为 \(\sum_{i=1}^N A_i\)

集合 \(S_1\)\(S_2\) 都非空

  • 如果 \(\min S_2 <= \max S_1\),那么可以将前 \(K\) 大的马都放在 \(B_i = 2\) 的位置后面,答案为 \(\sum_{i=1}^N A_i + maxKSum\)
  • 如果 \(\min S_2 > \max S_1\),那么只能将前 \(K - 1\) 大的马放在 \(B_i = 2\) 的位置后面,同时将 \(\max S_1\) 放在第一个位置,答案为 \(\sum_{i=1}^N A_i + \max S_1 - \min S_2 + maxKSum\)

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

#include <bits/stdc++.h>
using namespace std;

#define ls (p << 1)
#define rs (p << 1 | 1)
#define int long long

const int N = 2e5 + 5, M = 1e6 + 6, MAXN = 1e6;
int n, q, a[N], b[N];
struct tree { int cnt, sum; } t[M << 2];

void pushUp(int p)
{
    t[p].cnt = t[ls].cnt + t[rs].cnt;
    t[p].sum = t[ls].sum + t[rs].sum;
}

void modify(int pos, int val, int p = 1, int l = 1, int r = MAXN)
{
    if (l == r)
    {
        t[p].cnt += val;
        t[p].sum += pos * val;
        return;
    }
    int mid = (l + r) >> 1;
    if (pos <= mid)
        modify(pos, val, ls, l, mid);
    else
        modify(pos, val, rs, mid + 1, r);
    pushUp(p);
}

int query(int k, int p = 1, int l = 1, int r = MAXN)
{
    if (k <= 0)
        return 0;
    if (t[p].cnt <= k)
        return t[p].sum;
    if (l == r)
        return k * l;
    int mid = (l + r) >> 1;
    if (k <= t[rs].cnt)
        return query(k, rs, mid + 1, r);
    else
        return t[rs].sum + query(k - t[rs].cnt, ls, l, mid);
}

signed main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n >> q;
    multiset<int> s1, s2;
    int sum = 0;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i] >> b[i];
        if (b[i] == 1)
            s1.insert(a[i]);
        else
            s2.insert(a[i]);
        modify(a[i], 1);
        sum += a[i];
    }
    while (q--)
    {
        int w, x, y;
        cin >> w >> x >> y;

        modify(a[w], -1);
        if (b[w] == 1)
            s1.erase(s1.find(a[w]));
        else
            s2.erase(s2.find(a[w]));
        sum -= a[w];

        a[w] = x, b[w] = y;
        if (b[w] == 1)
            s1.insert(a[w]);
        else
            s2.insert(a[w]);
        modify(a[w], 1);
        sum += a[w];

        if (s2.empty())
        {
            cout << sum << "\n";
        }
        else if (s1.empty())
        {
            cout << 2 * sum - *s2.begin() << "\n";
        }
        else
        {
            int ans = 0;
            if ((*s2.begin()) > (*s1.rbegin()))
                ans = (*s2.begin()) - (*s1.rbegin());
            cout << sum + query(s2.size()) - ans << "\n";
        }
    }
    return 0;
}
posted @ 2026-01-18 16:28  nuo534202  阅读(4)  评论(0)    收藏  举报