• [牛客82B, 一起来做题第一场E]区间的连续段 (倍增,思维)


    首先考虑最暴力的做法。那复杂度是q * range的显然会爆。

    原本我是想用什么数据结构优化这个过程,但是想了想好像没什么东西处理划分这个操作。

    于是考虑观察这个操作的性质,我们发现对于每个范围,一旦L确定那么,在[L, R] 之间的划分数其实是固定的。(但是划分方案可能会有很多种)

    这里可以稍稍证明一下:

    假设区间为[L, R],那么最多的划分数就是R-L+1,之后我们为了尽可能减少划分数,我们必然要尽可能让一个划分集合与左右两边的集合合并。

    于是我们就利用贪心的思想,尽量让每一个划分集合都尽量大,那么我们就能做到划分数最小。

    于是我们解决了第一步,如何处理划分数的问题。但是我们不能通过预处理    divide_num[L][R]这个二维数组去得到答案。

    于是考虑优化掉数组第二维,该如何快速知道在L确定的情况下,到R的划分数呢?

    我们发现,最最开始,我们的划分数组存的其实都是到下一个划分点的位置,我们每次可以通过跳跃,去获得下一个划分点的位置,从而获得答案。

    但是最坏情况下就是,这个跳跃数都为1,即每个a[i] + a[i+1] > k, a[i] <= k。这样的话就会被卡成n2了。我们发现这个跳跃操作,其实是可以被优化的。

    考虑到跟树上倍增一样的思想,每次跳2的幂次,这样的话我们的复杂度就被降低到nlogn了。

    于是开始写。

    代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int inf = 0x3f3f3f3f; ///1061109567
    const int maxn = 2e6 + 10;
    
    int n, m, k;
    ll sum[maxn], a[maxn];
    int f[maxn][30], cant[maxn];
    
    int main() {
        scanf("%d%d%d", &n, &m, &k);
        for (int i = 1; i <= n; ++ i) {
            scanf("%lld", &a[i]);
            sum[i] = a[i] + sum[i-1];
            cant[i] = cant[i-1] + (a[i] > k);
        }
    
        for (int j = 0; j <= 20; ++ j) {
            for (int i = 1; i <= n; ++ i) {
                if (j == 0) {
                    int pos = upper_bound(sum+1, sum+1+n, sum[i-1]+k) - sum;
                    f[i][j] = pos;
                    //cout << "pos: " << pos << endl;
                }
                else f[i][j] = f[f[i][j-1]][j-1];
            }
        }
        while (m--) {
            int l, r; scanf("%d%d", &l, &r);
            if (cant[r] - cant[l-1] > 0) {
                puts("Chtholly");
                continue;
            }
    
            int ans = 1;
            ///至于ans为什么一开始是1,是因为我们记录的都是下一个划分数的位置,
            ///并且在寻找答案的时候让下一个划分数的位置<=R这说明最后一条我们是没有算在答案里面的,
            ///所以需要+1。
            for (int j = 20; j >= 0; -- j) {
                ///f[l][j] != 0的原因是因为l + 1<<j会大于n,
                ///这样的话倍增数组其实是没有给值的此时为0
                ///0 <= r 但是我们又不希望访问越界的位置。所以需要!=0
                if (f[l][j] && f[l][j] <= r) {
                    l = f[l][j];
                    ans += (1 << j);
                }
            }
    //        puts("");
            printf("%d
    ", ans);
    
        }
        return 0;
    }
  • 相关阅读:
    伪元素和伪类的区别
    绝对定位是相对定位元素的什么边界进行定位的?
    元素如何设置滚动/滚动条的设置与隐藏
    什么是行内元素、块级元素?
    通过JS判断当前浏览器的类型
    获取元素计算样式getComputedStyle()与currentStyle
    javascript链式运动框架案例
    任意值的运动框架
    JS多物体运动案例:变宽、变高
    offsetWidth与offsetHeight
  • 原文地址:https://www.cnblogs.com/Vikyanite/p/15361567.html
Copyright © 2020-2023  润新知