牛客82-B:区间的连续段 (ST表,贪心)

题目描述

给你一个长为n的序列a和一个常数k

有m次询问,每次查询一个区间[l,r]内所有数最少分成多少个连续段,使得每段的和都 <= k

如果这一次查询无解,输出"Chtholly"

输入描述:

第一行三个数n,m,k
第二行n个数表示这个序列a
之后m行,每行给出两个数l r表示一次询问

输出描述:

输出m行,每行一个整数,表示答案

输入

5 5 7
2 3 2 3 4
3 3
4 4
5 5
1 5
2 4

输出

1
1
1
2
2

sol:初看以为是线段树题,但是肯定会被卡。   我们用st表预处理,st[i][j]表示i点右移1<<j刀的最远距离。  和求LCA的道理一样,只要没到达边界,贪就完事了。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=1000010;
ll a[maxn]; int st[maxn][21],lg[maxn];
int main()
{
    int N,M,K,L,R,pos,res;
    scanf("%d%d%d",&N,&M,&K);
    rep(i,1,N) lg[i]=lg[i>>1]+1;
    rep(i,1,N) scanf("%lld",&a[i]);
    rep(i,1,N) a[i]+=a[i-1];
    rep(i,0,20) st[N+1][i]=N+1;
    for(int i=N;i>=1;i--){
        pos=upper_bound(a+1,a+N+1,a[i-1]+K)-a;
        st[i][0]=pos;
        rep(j,1,20) st[i][j]=st[st[i][j-1]][j-1];
    }
    rep(i,1,M){
        scanf("%d%d",&L,&R);
        pos=L;  res=0;
        for(int j=lg[R-L+1];j>=0;j--) {
            if(st[pos][j]<=R) pos=st[pos][j],res+=(1<<j);
        }
        if(st[pos][0]<=R) puts("Chtholly");
        else printf("%d\n",res+1);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/hua-dong/p/10820908.html