牛客练习赛14 B.区间的连续段 (倍增)

题目:传送门

题意

 

 

 

 思路

用 f[i][j] 表示,从 i 开始,分 2^j 个连续段,使得每段的和都 <= k 的右端点 + 1;

由于 a[i] >= 1,所以前缀和单调递增, 可以用二分求 f[i][0],然后 f[i][j] = f[f[i][j-1]][j-1];

对于每次询问,从 l 开始跳, j 从大向小枚举,若往右跳的点不超过 r,那就计算贡献,往右跳,从这个点接着往右跳。

#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define UI unsigned int
#define mem(i, j) memset(i, j, sizeof(i))
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define dep(i, j, k) for(int i = k; i >= j; i--)
#define pb push_back
#define make make_pair
#define INF 0x3f3f3f3f
#define inf LLONG_MAX
#define PI acos(-1)
#define fir first
#define sec second
#define lb(x) ((x) & (-(x)))
#define dbg(x) cout<<#x<<" = "<<x<<endl;
using namespace std;

const int N = 1e6 + 5;

int n, m, k;

LL a[N];

int f[N][21];

void solve() {

    scanf("%d %d %d", &n, &m, &k);

    rep(i, 1, n) scanf("%lld", &a[i]), a[i] += a[i - 1]; /// 维护前缀和

    rep(i, 0, 21) f[n + 1][i] = n + 1;

    dep(i, 1, n) {

        int pos = upper_bound(a + 1, a + 1 + n, a[i - 1] + k) - a;

        f[i][0] = pos;

        rep(j, 1, 20) f[i][j] = f[f[i][j - 1]][j - 1];

    }

    while(m--) {

        int l, r;

        scanf("%d %d", &l, &r);

        LL ans = 0LL;

        dep(i, 0, 20) {

            if(f[l][i] <= r) ans += (1 << i), l = f[l][i];

        }

        if(f[l][0] > r) printf("%lld\n", ans + 1);

        else puts("Chtholly"); 

    }

}


int main() {

//    int _; scanf("%d", &_);
//    while(_--) solve();

    solve();

    return 0;
}

 

posted on 2020-09-11 15:35  Willems  阅读(156)  评论(0编辑  收藏  举报

导航