BZOJ2567 : 籬笆

Claris發表於2019-02-12

設第$i$個區間的左端點為$a[i]$,區間長度為$len$,要覆蓋的部分的長度為$all$,因為區間左端點遞增,所以最優方案中它們的位置仍然遞增。

 

對於鏈的情況,要滿足三個條件:

1. 區間$i$可以接上區間$i-1$

設$f[i]$表示最優解中第$i$個區間左端點的位置,則$f[i]=\min(f[i-1]+len,a[i]+ans)$且$f[i]\geq a[i]-ans$。

所以$ans\geq\max(a[i]-f[i])=\max(a[i]-a[j]-ans-(i-j)\times len)$,

即$ans\geq\frac{\max(a[i]-a[j]-(i-j)\times len)}{2}(l\leq j\leq i\leq r)$。

2. 可以覆蓋位置$0$

即$f[l]\leq 0$,所以$a[i]+ans-(i-l)\times len\leq 0$,

故$ans\geq\max(a[i]-(i-l)\times len)$。

3. 可以覆蓋位置$all$

即$f[r]\geq all-len$,所以$a[i]+ans+(r-i)\times len\geq all-len$,

故$ans\geq\max(all-len-a[i]-(r-i)\times len)$。

 

對於環的情況,只需要把區間複製一份,那麼只需要滿足條件1就能保證沒有死角:

即$ans\geq\frac{\max(a[i]-a[j]-(i-j)\times len)}{2}(l\leq j\leq i\leq r)$,

且$ans\geq\frac{\max(a[i]+all-a[j]-(r-l+1-(j-i))\times len)}{2}(l\leq i<j\leq r)$。

 

以上所有資訊都可以通過線段樹進行區間合併,時間複雜度$O(m\log n)$。

 

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=120010,M=262150,BUF=30000000;
const ll inf=1LL<<60;
int T,n,m,type,i,x,y;ll all,r,len,a[N],b[N],ans;bool flag;char Buf[BUF],*buf=Buf;
struct P{
  ll v,w,ma,mi;
  void set(ll b){v=w=0,ma=mi=b;}
  P operator+(const P&b){
    P c;
    c.v=max(max(v,b.v),b.ma-mi);
    c.w=max(max(w,b.w),ma-b.mi);
    c.ma=max(ma,b.ma);
    c.mi=min(mi,b.mi);
    return c;
  }
}v[M],val;
inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;}
inline void read(ll&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;}
void build(int x,int a,int b){
  if(a==b){v[x].set(::b[a]);return;}
  int mid=(a+b)>>1;
  build(x<<1,a,mid),build(x<<1|1,mid+1,b);
  v[x]=v[x<<1]+v[x<<1|1];
}
void ask(int x,int a,int b,int c,int d){
  if(c<=a&&b<=d){
    if(!flag)val=v[x];else val=val+v[x];
    flag=1;
    return;
  }
  int mid=(a+b)>>1;
  if(c<=mid)ask(x<<1,a,mid,c,d);
  if(d>mid)ask(x<<1|1,mid+1,b,c,d);
}
inline void query(int x,int y){
  flag=0;
  ask(1,1,n,x,y);
  if(type==1){
    ans=val.v;
    ans=max(ans,(val.ma+len*x)*2);
    ans=max(ans,(all-len-val.mi-len*y)*2);
  }else ans=max(val.v,val.w-len*(y-x+1)+all);
  printf("%lld.%lld0000\n",ans/2,ans%2*5);
}
int main(){
  fread(Buf,1,BUF,stdin);read(T);
  while(T--){
    read(n),read(all),read(r),read(m),read(type);
    len=r*2;
    for(i=1;i<=n;i++)read(a[i]),a[i]-=r,b[i]=a[i]-len*i;
    build(1,1,n);
    query(1,n);
    while(m--)read(x),read(y),query(x,y);
  }
  return 0;
}