ProLink:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=4062
题意:有一列n个土地,每个土地经过会增长a[i]的价值,你可以走m步,让你最大化最小值
解题思路:最大化最小值,显然就是二分了,最大1e17,最小0,直接二分答案就行了
这题主要是check不好写。。
考虑一下check函数:对于一块地,肯定就是左右一直移动来让他的价值>=当前check的x,一直这样做下去。不过小细节有点多,我开了一个cnt数组直接来记录每块地达到这个值需要的最小次数,循环的时候标记一个flag,作用是判断当前这个点,要不要来回移动(因为肯定有一些点至于要一次,来回反而浪费了),还有,如果当前检查的时候ret就已经大于m +1了(至于为什么不是大于m,至于为什么要边跑边判,自己想),最后就是循环完了看一下最后一个的值,细节真的多。
Code:
#pragma GCC diagnostic error "-std=c++11"
#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<ext/rope>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define gcd __gcd
#define pb push_back
#define mp anske_pair
#define lowbit(x) x & (-x)
#define PII pair<int, int>
#define all(x) x.begin(), x.end()
#define rep(i, a, b) for(__typeof(b) i = a; i <= (b); i++)
#define FAST ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = (int)1e9 + 7;
const int maxn = (int)1e5 + 5;
using namespace std;
using __gnu_cxx::crope;
ll n, m;
int a[maxn];
ll cnt[maxn];
bool check(ll x){
ll ret = 0;
rep(i, 1, n + 1) cnt[i] = 0;
rep(i, 1, n){
cnt[i] = x / a[i];
if(x % a[i]) cnt[i]++;
}
bool flag = false;
rep(i, 1, n){
if(cnt[i] > 0){
ret += cnt[i] * 2;
cnt[i+1] -= cnt[i];
cnt[i] = 0;
if(!flag) flag = true;
else ret++, cnt[i+1]--;
}
else flag = false;
if(ret > m + 1) return false;
}
if(cnt[n+1]) ret--;
if(cnt[n] < 0) ret--;
if(ret <= m) return true;
return false;
}
int main()
{
int t; scanf("%d", &t);
while(t--){
scanf("%lld %lld", &n, &m);
rep(i, 1, n) scanf("%d", a + i);
if(n > m) { puts("0"); continue; }
ll l = 0, r = (ll)1e18;
ll ans;
while(l <= r){
ll mid = (l + r) >> 1ll;
//cout << mid << endl;
if(check(mid)){
l = mid + 1;
ans = mid;
}
else{
r = mid - 1;
}
}
printf("%lld\n", ans);
}
return 0;
}