题目链接:HDU3486
题意:给定一个
N
N
N个人的序列,按顺序切成
M
M
M组,每组
N
/
M
N/M
N/M个人,如果有多余的人则去掉(比如
14
14
14个人分成
4
4
4段,那么最后两个人是多余的,去掉),问:一个最小的
M
M
M,使得这
M
M
M组最大和严格大于
K
K
K。
分析:本题有求区间最值问题,那么我们可以想到用线段树或者
S
T
ST
ST表维护,但是本题不涉及区间的修改故用
S
T
ST
ST表较合理,并且简单,而且时间复杂度小。求最小的
M
M
M,可能我们会想到二分,但是本题是不满足二分的单调性的,因为不一定
M
M
M越大,我们得到的
M
M
M组最大和越大,比如这组数据:
N
=
9
N = 9
N=9,元素为
[
1
,
1
,
1000
,
2000
,
2
,
2
,
3000
,
4000
,
1
]
[1, 1, 1000, 2000, 2, 2, 3000, 4000, 1]
[1,1,1000,2000,2,2,3000,4000,1], 当M = 3时,
s
u
m
1
=
1000
+
2000
+
4000
=
7000
sum1 = 1000 + 2000 + 4000 = 7000
sum1=1000+2000+4000=7000, 当
M
=
4
M = 4
M=4时,
s
u
m
2
=
1
+
2000
+
2
+
4000
=
6003
,
s
u
m
1
>
s
u
m
2
sum2 = 1 + 2000 + 2 + 4000 = 6003, sum1 > sum2
sum2=1+2000+2+4000=6003,sum1>sum2, 所以只好暴力(优化)之。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200000 + 5;
int n, k, mx, sum, a[maxn], st[maxn][22];
void init(){
for(int i = 1; i <= n; i++) st[i][0] = a[i];
for(int j = 1; (1 << j) <= n; j++)
for(int i = 1; i + (1 << j) - 1 <= n; i++)
st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
}
int rmq(int l, int r){
int k = (int)((double)log(r - l + 1) / log(2.0));
return max(st[l][k], st[r - (1 << k) + 1][k]);
}
bool check(int o, int p){
int l, r, res, num;
l = 1, r = l + o - 1, num = res = 0;//[l, r]是每次查询的区间
while(r <= n && num < p){//要用一个num记录已经查询了多少个区间了, 不然可能会查询到多余的区间。
res += rmq(l, r);
l += o, r += o, num++;
if(res > k) return true;
}
return false;
}
int main(){
while(~scanf("%d %d", &n, &k)){
if(n == -1 && k == -1) break;
sum = 0, mx = 1;
for(int i = 1; i <= n; i++){
scanf("%d", a + i);
mx = max(mx, a[i]);
sum += a[i];
}
if(sum <= k){
puts("-1");
continue;
}
init();
for(int i = max(k / mx, 1); i <= n; i++){//暴力枚举组数不用从1开始,直接从(k/最大的元素)开始
if(check(n / i, i)){
printf("%d\n", i);
break;
}
}
}
return 0;
}
T h a n k s . Thanks. Thanks.