题目:传送门
题意
思路
用 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 ", ans + 1); else puts("Chtholly"); } } int main() { // int _; scanf("%d", &_); // while(_--) solve(); solve(); return 0; }