调和级数枚举+树状数组二分,CF 1997E - Level Up

一、题目

1、题目描述

2、输入输出

2.1输入

2.2输出

3、原题链接

E - Level Up

二、解题报告

1、思路分析

碎碎念

赛时看出了单调性,想到了树状数组二分,然而题目说等级比怪高怪会跑,我看成了等级比怪高才能打怪,唉,卡E卡了一小时,没能上大分


分析过程:

首先,值域在 2e5 而不是 1e9,说们本题存在和值域挂钩的做法

考虑在线没法做,应该得离线处理

由于是离线,想了下分块,但也只是想了下,然后接着观察条件

k = x,那么我们从头开始打,等级最高是 floor(n / x) + 1

要素察觉,应该会涉及调和级数枚举,然后接着分析

我们固定x,下标 i 处等级升级为LV,假设在下标 j 处等级升级为LV + 1,那么[i + 1, j]内恰好有x个数字的值 >= LV

假如我们把升级下标称为border,那么每个x,border的数目不超过 n / x,那么所有x的border数目之和不超过n ln n

如果当前等级为1,那么数组中所有怪都能打,因为他们都 >= 1,当我们升级为2级,我们将数组中所有等级为1的怪删去,那么剩下的怪又是全都能打的

考虑每个怪只会被删一次,这个是O(N)的,显然又涉及到了区间和维护,考虑树状数组

现在将已知信息综合,有如下做法来预处理每个 x 能够到达的所有等级以及升级下标:

外层循环枚举等级1~n,初始能够考虑的 k 为[1, n]

当前枚举等级为LV

考虑k = x,升级为LV时的下标为cur,找升级为LV + 1时的下标

由于数组中剩下的都是能打的,所以直接二分找到下标

考虑相当于找sum(j) - sum(cur) = x,相当于在树状数组中找第sum(cur) + x小的位置,我们可以用树状数组二分找第k小优化掉一个log(当然不优化也能过)

如果找到,就将 j 存入,把x加入下一次循环的k集合

然后处理完LV,将值为LV的怪兽都删掉,每个怪兽最多只被删一次,所以是O(NlogN)的

整体时间复杂度:O(NlnNlogN)

2、复杂度

时间复杂度: O(N lnN logN)空间复杂度:O(NlnN)

3、代码详解

 ​
#include <bits/stdc++.h>
// #define DEBUG
using i64 = long long;
using u32 = unsigned;
using u64 = unsigned long long;
constexpr int inf32 = 1e9 + 7;

template<typename T = int>
class FenWick {
private:
    int n;
    std::vector<T> tr;
public:
    FenWick(int _n) : n(_n), tr(_n + 1) 
    {}
    FenWick(const std::vector<T> &_init) : FenWick(_init.size()) {
        init(_init);
    }

    void init(const std::vector<T> &_init) {
        for (int i = 1; i <= n; ++ i) {
            tr[i] += _init[i - 1];
            int j = i + (i & -i);
            if (j <= n)
                tr[j] += tr[i];
        }
    }

    void add(T x, T k) {
        for (; x <= n; x += x & -x) tr[x] += k;
    }

    void add(T l, T r, T k) {
        add(l, k), add(r + 1, -k);
    }

    T query(T x) const {
        T res = T{};
        for (; x; x &= x - 1) res += tr[x];
        return res;
    }

    T query(T l, T r) const {
        if (l > r) return T{};
        return query(r) - query(l - 1);
    }

    int select(const T &k) {
        int x = 0;
        T cur{};
        for (int i = 1 << std::__lg(n); i; i /= 2) {
            if (x + i <= n && cur + tr[x + i] <= k) {
                x += i;
                cur = cur + tr[x];
            }
        }
        return x;
    }
};

constexpr int N = 2e5 + 10;
std::vector<int> val[N];
std::vector<int> lvl_up[N], rem;

void solve() {
    int n, q;
    std::cin >> n >> q;

    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
        std::cin >> a[i];
        val[a[i]].push_back(i + 1);
    }

    for (int i = 1; i <= n; ++ i) {
        rem.push_back(i);   //对于lv=0,所有x都能考虑
        lvl_up[i].push_back(0); //上一个位置都是0
    }

    FenWick tr(std::vector<int>(n, 1));

    for (int lv = 1; lv <= n; ++ lv) {
        std::vector<int> nrem;
        for (int x : rem) {
            int cur = lvl_up[x].back();
            if (cur == n) continue;

            int hi = tr.select(tr.query(cur) + x);

            if (hi <= n) {
                lvl_up[x].push_back(hi);
                nrem.push_back(x);
            }
        }

        rem = std::move(nrem);

        for (int i : val[lv])
            tr.add(i, -1);
    }

    while (q --) {
        int i, x;
        std::cin >> i >> x;
        -- i;

        if (lvl_up[x].size() <= a[i] || lvl_up[x][a[i]] > i)
            std::cout << "YES\n";
        else 
            std::cout << "NO\n";
    }

}

auto FIO = []{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    return 0;
} ();

int main() {
    #ifdef DEBUG
        freopen("in.txt", "r", stdin);
        freopen("out.txt", "w", stdout);
    #endif     

    int t = 1;
    // std::cin >> t;
    while (t --)
        solve();

    return 0;
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EQUINOX1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值