题意:给你一个长为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; }