题目
有n个人参加面试,公司只需要m个人,于是将n个人分成m段每段⌊nm⌋人,且从每段中选出能力最大的人。
有
n
个
人
参
加
面
试
,
公
司
只
需
要
m
个
人
,
于
是
将
n
个
人
分
成
m
段
每
段
⌊
n
m
⌋
人
,
且
从
每
段
中
选
出
能
力
最
大
的
人
。
现在要求计算出一个最小的m满足选出的m人的能力和严格大于k。
现
在
要
求
计
算
出
一
个
最
小
的
m
满
足
选
出
的
m
人
的
能
力
和
严
格
大
于
k
。
题解
Step1
S
t
e
p
1
网上很多使用二分的方法
其实是不对的,我们可以讨论一下二分为什么不对。
思路就是二分m,去判断选出的m人的和是否大于k
思
路
就
是
二
分
m
,
去
判
断
选
出
的
m
人
的
和
是
否
大
于
k
大于说明当前m是满足的,于是更改上界,再去判断是否存在更小的m也满足。
大
于
说
明
当
前
m
是
满
足
的
,
于
是
更
改
上
界
,
再
去
判
断
是
否
存
在
更
小
的
m
也
满
足
。
否则就更改下界,去寻找更大的m满足条件。
否
则
就
更
改
下
界
,
去
寻
找
更
大
的
m
满
足
条
件
。
这里默认了一个条件即m越大获得能力和越大。这也是能使用二分的单调性。
这
里
默
认
了
一
个
条
件
即
m
越
大
获
得
能
力
和
越
大
。
这
也
是
能
使
用
二
分
的
单
调
性
。
乍一看没什么不对,选的人多了嘛,肯定能力和越大。
可是实际上并不满足这个性质,因为题目是从一段中选出一个最大的。
比如例子3,4,55,55,2,3
比
如
例
子
3
,
4
,
55
,
55
,
2
,
3
当m=2时,答案为55+55=110;m=3时答案为4+55+3=62
当
m
=
2
时
,
答
案
为
55
+
55
=
110
;
m
=
3
时
答
案
为
4
+
55
+
3
=
62
并不满足单调递增性(m越大和越大)
并
不
满
足
单
调
递
增
性
(
m
越
大
和
越
大
)
所以二分是错误的,但是二分能过只能说数据不强,或者出题人有意放过。
所
以
二
分
是
错
误
的
,
但
是
二
分
能
过
只
能
说
数
据
不
强
,
或
者
出
题
人
有
意
放
过
。
Step2
S
t
e
p
2
那么接下来就来讨论正确的做法
既然不能二分,那只能从小到大枚举m了吧
既
然
不
能
二
分
,
那
只
能
从
小
到
大
枚
举
m
了
吧
使用ST表查询时间是O(1)的那么朴素的枚举m的时间复杂度为
使
用
S
T
表
查
询
时
间
是
O
(
1
)
的
那
么
朴
素
的
枚
举
m
的
时
间
复
杂
度
为
但是这里存在一个优化
但
是
这
里
存
在
一
个
优
化
我们知道⌊nm⌋只有n−−√种值。
我
们
知
道
⌊
n
m
⌋
只
有
n
种
值
。
也就是说对于不同的m,⌊nm⌋是一样的,即每一段的人数是一样的。
也
就
是
说
对
于
不
同
的
m
,
⌊
n
m
⌋
是
一
样
的
,
即
每
一
段
的
人
数
是
一
样
的
。
于是对于相同的⌊nm⌋我们没必要从头开始,只需要继续累计和即可
于
是
对
于
相
同
的
⌊
n
m
⌋
我
们
没
必
要
从
头
开
始
,
只
需
要
继
续
累
计
和
即
可
例子n=7
例
子
n
=
7
假设我们已经枚举到了m=4,⌊nm⌋=1
假
设
我
们
已
经
枚
举
到
了
m
=
4
,
⌊
n
m
⌋
=
1
也就是每1个人取一个最大值,现在已经取得了前4个人的值。
也
就
是
每
1
个
人
取
一
个
最
大
值
,
现
在
已
经
取
得
了
前
4
个
人
的
值
。
当m=5,,⌊nm⌋=1还是每1个人取一个最大值。
当
m
=
5
,
,
⌊
n
m
⌋
=
1
还
是
每
1
个
人
取
一
个
最
大
值
。
我们已经知道前4个人的最大值的和,对于第5个,只需要加上即可。
我
们
已
经
知
道
前
4
个
人
的
最
大
值
的
和
,
对
于
第
5
个
,
只
需
要
加
上
即
可
。
而对于⌊nm⌋出现的范围是[m,n⌊nm⌋]
而
对
于
⌊
n
m
⌋
出
现
的
范
围
是
[
m
,
n
⌊
n
m
⌋
]
时间复杂度为
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
typedef long long ll;
int arr[maxn],mm[maxn];
int n;ll k;
struct ST{
int dp[maxn][20];
void init() {
mm[0] = -1;
for(int i=0;i<n;i++) {
dp[i][0] = arr[i];
mm[i+1] = (((i+1) & i) == 0) ? mm[i] + 1 : mm[i];
}
for(int j=1;(1<<j)<=n;j++) {
for(int i=0;i+(1<<j)-1<n;i++) {
dp[i][j] = max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
}
int query(int l,int r) {
int k = mm[r-l+1];
// while((1<<(k+1)) <= (r - l + 1)) k++;
return max(dp[l][k],dp[r-(1<<k)+1][k]);
}
}st;
int solve() {
st.init();
for(int m=2;m<=n;) {
int len = n / m,c = 1,l;
int r = n / len;
ll res = 0;
for(;m<=r;m++) {
for(;c<=m;c++) {
l = c * len;
res += st.query(l-len,l-1);
if(res > k) return m;
}
}
}
return -1;
}
int main()
{
while(~scanf("%d%lld",&n,&k),n != -1 && k != -1) {
ll sum = 0;
int tmp = 0;
for(int i=0;i<n;i++) {
scanf("%d",&arr[i]);
sum += arr[i];
tmp = max(tmp,arr[i]);
}
if(sum <= k) {
printf("-1\n");
continue;
}
if(tmp > k) {
printf("1\n");
continue;
}
printf("%d\n",solve());
}
return 0;
}