66 E Petya and Post

問題

環状の道路にn個のガソリンスタンドが並んでいる。1番のガソリンスタンドはn番と2番のガソリンスタンドと隣接している。
それぞれのガソリンスタンド間の距離はa[i]、それぞれのガソリンスタンドで供給できるガソリンの量b[i]が与えられる。

あるガソリンスタンドを出発して、環状の道路を一周できるかどうかを知りたい。
一周するためには、途中でガソリンの量がマイナスになってはならない。
一周できるガソリンスタンドの番号を答えよ。(回り方は時計回り、反時計回りのどちらでもよい)

制約条件

n≦10^5
a[i],b[i]≦10^9
a[i]の和はb[i]の和に等しい。

方針

1番のスタンドから出発して、i番目のスタンドに辿り着く直前でのガソリンの量はΣa[i]-b[i]とかける。
これが全てのiについて非負であるようなスタンドを求めたい。
これを愚直にやるとO(n^2)かかってしまうのでTLE.


双方向キューを使い、数列S[i]=Σa[i]-b[i]について、蟻本に載っている、スライド最小値の技法を使って計算することでO(n)時間で計算できる。
n個のスタンドからはみ出た部分は後で引いて調整する。

ソースコード

int n,a[200000],b[200000],sum[200000],deq[200000];
int sz,ans[200000];
bool ok[200000];
void run(){
  scanf("%d",&n);
  rep(i,n)scanf("%d",a+i),a[i+n]=a[i];
  rep(i,n)scanf("%d",b+i),b[i+n]=b[i];
  
  rep(it,2){
    rep(i,2*n)sum[i]=a[i]-b[i];
    rep(i,2*n-1)sum[i+1]+=sum[i];
    
    int s=0,t=0,minus=0;
    rep(i,2*n-1){
      while(s<t&&sum[deq[t-1]]>=sum[i])t--;
      deq[t++]=i;
      if(i-n+1>=0){
        if(sum[deq[s]]-minus>=0)ok[it?n-1-(i-n+1):i-n+1]=1;
        minus+=a[i-n+1]-b[i-n+1];
        if(deq[s]==i-n+1)s++;
      }
    }
    if(!it)reverse(a,a+2*n),reverse(b,b+2*n),rotate(b,b+1,b+2*n);
  }
  rep(i,n)if(ok[i])ans[sz++]=i+1;
  printf("%d\n",sz);
  rep(i,sz)printf("%d%c",ans[i],i==sz-1?'\n':' ');
}