nowcoder82B 区间的连续段

题意:给你一个长为n的序列a和一个常数k, 有m次询问,每次查询一个区间[l,r]内所有数最少分成多少个连续段,使得每段的和都 <= k (1 <= n , m <= 1000000 , 1 <= ai , k <= 1000000000)

题解:预处理,对于一个区间,它分的段数可以进行贪心进行处理,这里又用到了倍增的思想,dp[i][j]代表以i为起点的分2^j段的末尾下标,可以进行递推dp[i][j] = dp[dp[i][j-1]][j-1];

#include <bits/stdc++.h>
#define maxn 1000010
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
ll n, m, k, a[maxn], sum[maxn], dp[maxn][21];
int main(){
    memset(dp, INF, sizeof(dp));
    scanf("%lld%lld%lld", &n, &m, &k);
    for(ll i=1;i<=n;i++)
        scanf("%lld", &a[i]);
    for(ll i=1;i<=n;i++)
        sum[i] = sum[i-1]+a[i];
    for(ll i=1;i<=n;i++){
        ll l = i, r = n, p = -1;
        while(l<=r){
            ll mid = (l+r)>>1;
            if((sum[mid]-sum[i-1])<=k)
                l = mid+1, p = mid;
            else r = mid-1;
        }
        if(p != -1) dp[i][0] = p;
    }
    for(ll j=1;j<=20;j++){
        for(ll i=1;i<=n;i++)
            if(dp[i][j-1]+1 <= n)
                dp[i][j] = dp[dp[i][j-1]+1][j-1];
    }
    ll x, y, ans;
    while(m--){
        scanf("%lld%lld", &x, &y);
        ans = 0;
        for(ll i=20;i>=0;i--){
            if(dp[x][i] <= y){
                ans += (1<<i);
                x = dp[x][i]+1;
            }
        }
        if(x<=y&&dp[x][0] >= y) ans++;
        if(sum[y]-sum[x-1] > k) printf("Chtholly
");
        else printf("%lld
", ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Noevon/p/8694905.html