BZOJ3807 : Neerc2011 Lanes

左右与右左是两个独立的问题

设f[i]表示i时刻左右车道减少一条的答案

g[i]表示i时刻右左车道增加一条的答案

ans=min(f[i]+g[i+r])

计算f[i]:

首先暴力计算出f[m+1],同时记录下每个时刻刚开走的车的数量now[i]

从m到1计算f[i],如果该时刻开走的车数不足n1+1,则无影响

否则多了一辆车,选取后面最早的有空位的时刻j开走

用一个栈维护空位,复杂度为$O(m)$

计算g[i]:

首先暴力计算出g[0],同时记录下每个时刻刚开走的车的数量now[i]

从1到m+r计算g[i],如果该时刻开走的车数不足n2+1,则无影响

否则多了一辆车,选取后面最早的有空位的时刻j开走

因为j是单调变化的,所以复杂度为$O(m)$

#include<cstdio>
#define N 200010
typedef long long ll;
struct P{int t,k;P(){}P(int _t,int _k){t=_t,k=_k;}}q[N];
int n1,n2,m,r,i,a[N],b[N],now[N],rem,t,mx,fin;ll ans,f[N],g[N];
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
inline int min(int a,int b){return a<b?a:b;}
int main(){
  for(read(n1),read(n2),read(m),read(r),i=1;i<=m;i++)read(a[i]),read(b[i]);
  for(rem=ans=0,i=1;i<=m;i++)rem+=a[i],t=min(rem,n1+1),now[i]=t,rem-=t,ans+=rem;
  mx=m+rem/n1+1,fin=rem%n1;
  if(fin)ans+=(ll)(fin+rem-n1)*(rem/n1)/2;else ans+=(ll)rem*(rem/n1-1)/2;
  for(q[t=1]=P(mx,fin),i=m;i;i--){
    if(now[i]>n1){
      while(t&&q[t].k==n1)t--;
      if(!t)q[t=1]=P(++mx,0);
      q[t].k++,ans+=q[t].t-i;
    }else q[++t]=P(i,now[i]);
    f[i]=ans;
  }
  for(rem=ans=0,i=1;i<=m+r;i++)rem+=b[i],t=min(rem,n2+1),now[i]=t,rem-=t,ans+=rem;
  mx=m+r+rem/(n2+1)+1,fin=rem%(n2+1);
  if(fin)ans+=(ll)(fin+rem-n2-1)*(rem/(n2+1))/2;else ans+=(ll)rem*(rem/(n2+1)-1)/2;
  for(i=t=1;i<=m+r;i++){
    g[i]=ans;
    if(now[i]>n2){
      while(t<=m+r&&(t<i||now[t]>n2))t++;
      if(t<=m+r)now[t]++,ans+=t-i;else{
        if(fin>n2)mx++,fin=0;
        fin++,ans+=mx-i;
      }
    }
  }
  for(ans=1LL<<62,i=1;i<=m;i++)if(f[i]+g[i+r]<ans)t=i,ans=f[i]+g[i+r];
  return printf("%d",t),0;
}

  

原文地址:https://www.cnblogs.com/clrs97/p/4403181.html