1. 程式人生 > 其它 >P4343 [SHOI2015]自動刷題機

P4343 [SHOI2015]自動刷題機

找最大值:二分到一個n的值t,然後根據日誌檢查在t的情況下能切幾題,如果滿足切的題目數>=k,那麼所有比t的值都能夠使得切題數>=k,此時可能可以找到n的最大值,因為如果不存在一個t能使切題數正好是k的話,找到的值是最大的能夠使切題數>k的值

找最小值:二分到一個n的值t,如果滿足切的題目數<=k,那麼所有比t的值都能夠使得切題數<=k,此時可能可以找到n的最小值,因為如果不存在一個t能使切題數正好是k的話,找到的值是最小的能夠使切題數<=k的值

複雜度:\(O(l \log N)\), 其中N是n的二分範圍大小,一開始取得範圍很大(\(\log N\)

大約是60的樣子),T掉了,後來把範圍縮小成\([1, 2^{41}]\),就是\(\log N = 41\)過了

另外還需要注意的就是,先找最小值,找到以後需要特判一下二分出來的最小值能不能讓切題數正好是k,不能就直接輸出-1

#include<iostream>
#include<cstring>
using namespace std;

const int N = 100010;

#define LL long long

LL a[N];
int ll, k;

LL check(LL x){
    LL cnt = 0;
    int res = 0;
    for(int i = 0; i < ll; i ++){
        cnt = max(cnt + a[i], (LL) 0);
        if(cnt >= x) res ++, cnt = 0;
    }
    return res;
}

int main(){
    cin >> ll >> k;
    for(int i = 0; i < ll; i ++) cin >> a[i];
    
    LL l = 1, r = (LL) 1 << 41;
    while(l < r){
        LL mid = l + r >> 1;
        if(check(mid) <= k) r = mid; // 如果切題的個數滿足<=k
        else l = mid + 1;
    }
    
    if(check(l) != k){
        puts("-1");
        return 0;
    }
    
    cout << l << ' ';
    
    l = 1, r = (LL) 1 << 41;
    while(l < r){
        LL mid = l + r + 1 >> 1;
        if(check(mid) >= k) l = mid;
        else r = mid - 1;
    }
    
    cout << l;
    
    return 0;   
}