洛谷 P2629 好訊息,壞訊息 題解
阿新 • • 發佈:2021-12-16
暴力演算法的時間複雜度是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; }