1. 程式人生 > 其它 >洛谷 P2629 好訊息,壞訊息 題解

洛谷 P2629 好訊息,壞訊息 題解

暴力演算法的時間複雜度是O(n^2),考慮優化;

先匯入一種思想——斷環為鏈。說通俗點就是在原陣列後面再接上下標為1——(n - 1)的元素;

以樣例為例:-3 5 1 2;我們將其斷環為鏈後可以得到這樣的一維陣列:-3 5 1 2 -3 5 1;
設其下標為1——7;當k=1時,判斷下標1——4;當k=2時,判斷下標2——5;當k=3時,判斷下標3——6;當k=4時,判斷下標4——7 結束;

斷環為鏈後,題目要求就變為尋找k的個數,使k可以滿足k——(n + k - 1)中,每個元素的對應的區間內字首和都是非負的;
對此,我們在使用字首和預處理後,就只需判斷每個s[i] - s[k - 1] (k <= i <= n + k - 1)是否為負就可以;

既然這樣,那麼是否要判斷k——n+k-1中每一個數的和呢?當然不是,因為其中如果只要有一個元素的對應的區間內字首和是負的,那麼這個k就是不符合的;
所以我們只需要判斷一次———判斷最小的s[i]減去s[k-1]是否為負。

總的來說:首先斷環為鏈,其次字首和處理,最後維護一個單調佇列,找到最小的s[i],判斷即可;
程式碼如下:

#include<cstdio>
#include<iostream>
using namespace std;
int n,head=1,tail,ans;
long long a[2000001],s[2000001],q[2000001];
int main()
{
    scanf("%d",&n);
    for(register int i=1;i<=n;i+=1)
        scanf("%lld",&a[i]);
    for(register int i=1;i<=n-1;i+=1)
        a[i+n]=a[i];
    for(register int i=1;i<=2*n-1;i+=1)
        s[i]=s[i-1]+a[i];
    for(register int i=1;i<=2*n-1;i+=1)
    {
        while(head<=tail&&max(i-n+1,1)>q[head])head++;
        while(head<=tail&&s[i]<=s[q[tail]])tail--;
        q[++tail]=i;
        if(i-n+1>0&&s[q[head]]-s[i-n]>=0)ans++;
    }
    printf("%d\n",ans);
    return 0;
}