1. 程式人生 > >Business Cycle 【UVALive - 7501】【二分答案+思維處理】

Business Cycle 【UVALive - 7501】【二分答案+思維處理】

題目連結


  14年的EC(銀牌題),但是現在的大牛們進步神速,估計如今已經是道銅牌題了,具體我們先講一下題意。

  一個長度為N的自環圈,每個點(1~N)上有自己對應的權值(可能為負數),我們用一個初始值進入這個環,每次走到一個節點的時候會加上這個節點的權值,如果此時的權值<0,就為0了,問P步之內是否存在一點使得權值>=G。問這樣的投入值的最小值。

  思路

  既然我們要求的是這樣的答案最小值,我們不妨可以二分答案試試,我們列舉這樣的答案,放進這個環裡去,要是出現了權值>G的就直接返回true就是了,然後,怎麼去處理P可能為1e18這麼大的一個值?我們得去尋它的週期,既然是環,就有自己的週期,可能為負,可能為0,也可能為正,這就是我們需要判斷的東西了。

  一個週期下來,我們可能沒發比較,因為舉個例子{ N=3; a[] = {-50, -90, 100}; }這樣的環,一個週期下來,上升了100!可是這不是真實值,當下個週期來臨的時候,會發現對應100的那個點還是100,其餘點還是0,那豈不是沒有改變!所以,週期不應當如此來算,我們得跑兩個環來算週期差

  算出週期的差值之後,會發現週期差值會“<=0”或者“>0”,前者的話,跑兩圈之後,與跑兩圈以內就沒有任何差別了,所以都不需要再去判斷了;後者,那麼如果我們的步數P足夠大的話,可以跑完兩圈再繼續跑的話,我們就可以以第二圈為基礎,往後繼續跑相應的路程了,但是後者就又要相關的處理了,我們跑到最後幾圈的時候就得開始慎重了,當最後一個完整圈

的時候,我們得先去這個完整圈子裡看看有沒有符合條件的最大值,然後再去看下剩餘的不足一個完整圈的那幾步中的最大值,於是只需要判斷最大值是否大於等於G即可了。

  大綱

  1. 先跑兩個週期,找週期差值;
  2. 判斷週期差值的範圍「<=0 or >0」以及P與N的關係,是否是兩圈之內就可以走完;
  3. 如果週期差值>0以及p>=2*N,就說明,可以走兩圈以上,並且最大值可能會出現在後面的節點上,於是就要跑最後一個完整週期以及跑完完整週期後的剩餘幾步中找尋最大值。

  具體的步驟就這麼多,但是處理的方式可就考驗思維了,一開始寫了一大長串,還WA、T,然後就決定把它們放進兩個函式裡去處理,一下子方便了很多(雖然時間複雜度高了)。


#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxN = 1e5 + 7;
ll N, G, P, a[maxN] = {0}, b[maxN] = {0};
ll lound(ll key)
{
    ll pre = key;
    for(int i=1; i<=N; i++)
    {
        pre += a[i];
        if(pre < 0) pre = 0;
    }
    return pre;
}
ll lound_Maxx(ll pos, ll key)
{
    ll pre = key, ans = key;
    for(int i=1; i<=pos; i++)
    {
        pre += a[i];
        if(pre < 0) pre = 0;
        ans = max(ans, pre);
    }
    return ans;
}
bool solve(ll num)
{
    ll ans = num;
    ll lound_1 = lound(num);
    ll lound_2 = lound(lound_1);
    if(lound_1 >= lound_2 || P < 2*N)
    {
        if(P > N)
        {
            if(P < 2*N) ans = max(ans, lound_Maxx(P - N, lound_1));
            else ans = max(ans, lound_Maxx(N, lound_1));
        }
        else ans = max(ans, lound_Maxx(P, num));
        return ans >= G;
    }
    ll tims = P/N, rest = P%N, sum_of = lound_2 - lound_1;
    if((tims - 1) > (G - lound_1)/sum_of) return true;
    ll ans_1 = 0, ans_2 = 0;
    if(tims > 1) ans_1 = (tims - 1)*(lound_2 - lound_1) + lound_1;
    else ans_1 = lound_1;
    ans_1 = lound_Maxx(rest, ans_1);
    if(tims > 2) ans_2 = (tims - 2)*(lound_2 - lound_1) + lound_1;
    else ans_2 = lound_1;
    ans_2 = lound_Maxx(N, ans_2);
    ans = max(ans, max(ans_1, ans_2));
    return ans >= G;
}
int main()
{
    int T;  scanf("%d", &T);
    for(int Cas=1; Cas<=T; Cas++)
    {
        scanf("%lld%lld%lld", &N, &G, &P);
        for(int i=1; i<=N; i++) scanf("%lld", &a[i]);
        ll L = 0, R = G, mid = 0, ans = G;
        while(L <= R)
        {
            mid = (L + R)>>1;
            if(solve(mid))
            {
                R = mid - 1;
                ans = mid;
            }
            else L = mid + 1;
        }
        printf("Case #%d: %lld\n", Cas, ans);
    }
    return 0;
}