本题使用倍增的思路去做,如果不考虑区间问题要想分成最少个连续段使每段的和都<=k,那么就需要从第一位开始贪心的跳跃到最远的那个下标处。
那么我们按照这个思路对每一位数都可以得到一个可跳跃的最远下标。那么结合倍增,如果可以的话我们甚至可以直接跳跃1段,两段,四段。。。。那么在某一段里面到底能跳多少段其实是拿可以跳的最大的段,然后逐步缩小。最后就可以得到到底最少可以跳跃几段。倍增的递推式为:dp[i][j] = dp[dp[i][j-1]][j-1];。要注意跳跃会超过n所以要对n+1地方进行处理。还有就是需要从大向小遍历数,因为前面的数需要后面数跳跃距离的结论。
#include <bits/stdc++.h> using namespace std; #define int long long const int maxn = 1000000+10; int n, m, k; int dp[maxn][22]; int a[maxn]; int cnt[maxn]; signed main() { scanf("%lld %lld %lld", &n, &m, &k); for (int i=1;i<=n;i++) { scanf("%lld", &a[i]); } //求一个前缀和 for (int i=1;i<=n;i++) { cnt[i] = cnt[i-1]; cnt[i] += a[i]; } //如果超过。 for (int i=0;i<=20;i++) { dp[n+1][i] = n+1; } //开始倍增预处理 for (int i=n;i>=1;i--) { //初始跳跃1,也就是他自己 // dp[i][0] //去寻找最远的跳跃下标的下一个 dp[i][0] = upper_bound(cnt+i, cnt+n+1, k+cnt[i-1])-cnt; //开始倍增跳跃 for (int j=1;j<=21;j++) { dp[i][j] = dp[dp[i][j-1]][j-1]; } } int l, r; while (m--) { scanf("%lld %lld", &l, &r); int ans = 0; for (int i=21;i>=0;i--) { if (dp[l][i]<=r) { ans += (1<<i); l = dp[l][i]; } } if (dp[l][0]>r) cout<<ans+1<<endl; else cout<<"Chtholly\n"; } return 0; }