三十年河东,三十年河西|

自动机

园龄:1年10个月粉丝:2关注:4

Codeforces Round 894 (Div. 3)

这次div3,没开出来D太令人伤心了

D

题意:

假设你有 x 个冰淇淋球, 每两个冰淇淋球可以做一个双球冰淇淋,注意{1, 2}和{2, 1}是相同的,问你想做 n 种不同的双球冰淇淋最少需要多少个冰淇淋球

思路:

要想冰淇淋球最少肯定优先用不同的来组合,所以可以先处理出来小于等于 n 的最大C(2, x), 然后不够的我们往里面添加相同的数
比如{1, 2, 3}我们再往里面加入一个1就会发现方案数加一, 再加一个2方案数又加一, 因为,C(2, n + 1) - C(2, n) <= C(2, n + 1)

#include <bits/stdc++.h>

using namespace std;
#define more ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl "\n"
#define pb push_back;
const int N = 2e5 + 5, MOD = 1e9 + 7, inf = 0x3f3f3f3f;
const long long INF = 1e18;
typedef long long LL;
typedef unsigned long long usLL;
typedef pair<int, int> PII;
typedef pair<string, string> PSS;
typedef pair<int, string> PIS;

LL C(LL n)
{
    return (n - 1) * n / 2;
}

inline void solve() 
{
    LL n; cin >> n;

    LL l = 2, r = 2e10;
    while (l < r)
    {
        int mid = (l + r + 1) / 2;
        if (C(mid) <= n) l = mid;
        else r = mid - 1;
    }

    if (C(l) == n) cout << l << endl;
    else cout << n - C(l) + l << endl;
}

int main()
{
    more;
    int T;
    cin >> T;
    while (T--)
    {
        solve();
    }
    //solve();
    return 0;
}

E

题意:

给你一个序列 A, 你可以从中选最多 m 个数,每选一个数都需要付出代价,代价为当前这个数与上次选择的那个数之间的距离乘以 d, 问你最大能获得的数字和是多少, 假定拿第一个数付出的代价为d

思路:

假设现在选了 k 个数, = ai1 - (d * i1) + ai2 -(d * (i2 - i1))....aik - (d * (ik - ik1)) = (a1 + a2 + ...ak) - (d * ik)), 可以发现付出的代价之和之和最后一个数有关,所以枚举最后一个选的数。同时维护一个长度为 m 的优先队列,只留前 m 大的数。

inline void solve() 
{
    int n, m, d; cin >> n >> m >> d;
    std::vector<int> a(n);
    for (int i = 0; i < n; i++)
        cin >> a[i];

    priority_queue<int, vector<int>, greater<int>> q;
    LL sum = 0,  ans = 0;
    for (int i = 0; i < n; i++)
    {
        if (a[i] < 0) continue;

        q.push(a[i]);
        sum += a[i];

        if (q.size() > m) // 超出部分删掉
        {
            sum -= q.top();
            q.pop();
        }
        ans = max(ans, sum - (LL)(i + 1) * d);
    }
    cout << ans << endl;
}

F

题意:

你有两个魔法,水魔法和火魔法。每秒你分别能获得 w, f点对应的魔力。
现在有 n 个怪物,每个怪物的强度为 k。强度为 k 的怪物需要 k 点魔力的任意魔法消灭。
现在施法不耗任何时间,请问几秒后可以杀完全部的怪物?

思路:

所有的怪最后都是一部分被水魔法打败,一部分被火魔法打败,那么可以枚举其中一部分,n 为 100 递归会爆,二进制枚举也会炸。
可以用bitset优化,一旦知道哪些怪是水魔法打败的,那么花费的时间就是 / cnt,火魔法的时间也可以直接除出来

inline void solve() 
{
    int w, f; cin >> w >> f;
    int n; cin >> n;
    std::vector<int> s(n);
    for (int i = 0; i < n; i++)
        cin >> s[i];
    
    std::bitset<N> dp{};
    dp[0] = 1;

    int sum = 0;
    for (int i = 0; i < n; i++) // 预处理
    {
        dp |= dp << s[i];
        sum += s[i];
    }

    int ans = inf;
    for (int i = 0; i <= sum; i++) 
    {
        if (dp[i]) // 说明i可以由一些s[i]加起来得到
        {
            ans = min(ans, max((i + w - 1) / w, (sum - i + f - 1) / f));
        }
    }
    cout << ans << endl;
}

G

题意:

定义一个程序为:

  1. 将数组非降序排序并去重
  2. 数组中只有一个数时终止程序并输出这个数
  3. 给数组每个数加上n - i + 1, 下标从1开始
  4. 重复上述操作
    现在有个q个查询,每次输入x, y; 将a[x]改为y,并将数组输入程序输出最后的值

思路:

通过观察可以发现:

  1. 每次执行操作3之后,数组的差分数组会整体减一
  2. 差分为0的数会被去重
  3. 假设刚开始的时候最大的差分数值为d,那么操作3一共会被执行d次
  4. 当初始数组sort之后,最大值为an, 程序结束之后输出的值为 an + d
    所以我们需要查询数组最大值和最大差分值,可以O(n)查询,总的复杂度为O(q * n)超时
    我们可以使用两个multiset来维护序列,s维护最大值,d维护最大差分
    修改操作可以认为是删除a[x],再插入y
inline void solve() 
{
    int n; cin >> n;
    std::vector<int> a(n);

    for (int i = 0; i < n; i++)
        cin >> a[i];

    multiset<int> s, d{0};

    auto add = [&](int x) // 插入一个数会影响它和前缀的差分,它和后缀的差分,前缀和后缀的差分
    {
        auto it = s.insert(x);
        auto r = next(it);

        if (it != s.begin()) // 存在前缀
        {
            d.insert(x - *prev(it));
        }

        if (r != s.end())
        {
            d.insert(*r - x);
        }

        if (it != s.begin() && r != s.end())
        {
           d.erase(d.find(*r - *prev(it))); // 要删除迭代器,不能直接删除数值,不然会把重复的也删掉
        }
    };

    auto del = [&](int x)
    {
        auto it = s.find(x);
        auto r = next(it);
        if (it != s.begin())
        {
            d.erase(d.find(x - *prev(it)));
        }
        if (r != s.end())
        {
            d.erase(d.find(*r - x));
        }

        if (it != s.begin() && r != s.end())
        {
            d.insert(*r - *prev(it));
        }

        s.erase(it);
    };


    for (int i = 0; i < n; i++)
        add(a[i]);

    int q; cin >> q;
    while (q--)
    {
        int x, y; cin >> x >> y;
        x--;
        del(a[x]);
        a[x] = y;
        add(a[x]);
        cout << *s.rbegin() + *d.rbegin() << ' ';
    }

    cout << endl;
}

本文作者:自动机

本文链接:https://www.cnblogs.com/monituihuo/articles/17658892.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   自动机  阅读(4)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起