题目大意:
给你 n 个数,让你分成 m 组,每组长度 n / m,剩下的人不要。每组最大值之和要大于给出的 k,问最小的 m 是多少。
**输入:**输入多个测试用例。每个测试用例的第一行包含两个数字n和k,表示人数和每个人的能力值和(n≤200000,k≤1000000000);第二行都包含n个数值,表示每个人的能力值。以两个-1结束,不处理。
输出:
对每个测试用例,都单行输出可以找到的最小m。若找不到,则输出-1。
输入样例
11 300
7 100 7 101 100 100 9 100 100 110 110
-1 -1
输出样例:
3
题解:本题属于RMQ问题,用ST解决。假设知道最小的分段数m,其m个分段的最大值之和必然大于k。问题是不知道最小分段数m,可以采用二分法搜索找到m。
#include<bits/stdc++.h>
using namespace std;
#define MAXN 200000+1
int lb[MAXN];
int F[MAXN][20];
int a[MAXN];
int n,k;
void Initlog()
{
lb[0]=-1;
for(int i=1;i<MAXN;i++)
lb[i]=(i&(i-1))?lb[i-1]:lb[i-1]+1;
}
void ST_create(int n)
{
for(int j=1;j<=lb[n];j++)
for(int i=1;i<=n-(1<<j)+1;i++)
F[i][j]=max(F[i][j-1],F[i+(1<<(j-1))][j-1]);
}
int RMQ(int l,int r)
{
if(l>r) return 0;
int k=lb[r-l+1];
return max(F[l][k],F[r-(1<<k)+1][k]);
}
bool ok(int m) //判断分成m段是否可行
{
int t=n/m,s=0;
for(int i=0;i<m;i++)
s+=RMQ(t*i+1,t*i+t); //查询每段的最大值累加
return s>k;
}
void slove(int n) //二分查找最小分段数
{
int l=1,r=n,ans;
while(l<=r){//二分
int m=(l+r)>>1;
if(ok(m)) r=m-1,ans=m;
else l=m+1;
}
printf("%d\n",ans);
}
int main()
{
int sum_a=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
F[i][0]=a[i];
sum_a+=a[i];
}
Initlog();
if(sum_a<=k) printf("-1\n");
else
ST_create(n);
slove(n);
}
/*
11 300
7 100 7 101 100 100 9 100 100 110 110
*/