题干:
链接:https://ac.nowcoder.com/acm/contest/82/B
来源:牛客网
给你一个长为n的序列a和一个常数k
有m次询问,每次查询一个区间[l,r]内所有数最少分成多少个连续段,使得每段的和都 <= k
如果这一次查询无解,输出"Chtholly"
输入描述:
第一行三个数n,m,k
第二行n个数表示这个序列a
之后m行,每行给出两个数l r表示一次询问
输出描述:
输出m行,每行一个整数,表示答案
示例1
输入
复制
5 5 7
2 3 2 3 4
3 3
4 4
5 5
1 5
2 4
输出
复制
1
1
1
2
2
备注:
对于100%的数据,1 <= n , m <= 1000000 , 1 <= ai , k <= 1000000000
解题报告:
首先发现可以贪心,这样是O( nm )的
由于k固定,考虑数组中每个位置i向最大的j+1使得a[i..j]的和<=k连边。这个连边的结构是个森林,每次查询即查询树的一条链,可以倍增维护。O( nlogn + mlogn )
AC代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define FF first
#define SS second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair<int,int> PII;
const int MAX = 1e6 + 5;
int n,m;
ll K,a[MAX],sum[MAX];
int f[MAX][22];
int main()
{cin>>n>>m>>K;for(int i = 1; i<=n; i++) scanf("%lld",a+i),sum[i] = sum[i-1] + a[i];for(int i = 1; i<=n; i++) {int pos = upper_bound(sum+i,sum+n+1,sum[i-1]+K) - sum;f[i][0] = pos; }for(int j = 0 ; j<=21; j++) f[n+1][j] = n+1;for(int j = 1; j<=21; j++) {for(int i = 1; i<=n; i++)f[i][j] = f[f[i][j-1]][j-1];}while(m--) {int l,r;scanf("%d%d",&l,&r);int ans = 0;for(int j = 21; j>=0; j--) {if(f[l][j] <= r) ans += (1<<j),l = f[l][j];}if(f[l][0] > r) {printf("%d\n",ans+1);}else printf("Chtholly\n");}return 0 ;
}