BZOJ3898 : 打的士

设$f_i$表示选择的答案区间左端点为$i$时,区间长度最小是多少。

那么每来一批人的时候,设$nxt$为$i$右边最近的一个可行决策,则$f_i=\max(f_i,nxt-i)$。

注意到$f$的形式是一条条斜率为$-1$的线段,且截距单调不下降,故每次修改可以转化为对截距的区间赋值。

用线段树维护$f$,对于一个区间,如果无法覆盖最左端,则返回,如果可以覆盖最右端,则打标记,否则暴力递归左右儿子。

时间复杂度$O(n\log n)$。

 

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1000010,M=2100000,inf=2000000010;
int n,i,j,x,y,q[N][2],st[N],en[N],m,b[N],e[N],k,a[N],l[M],r[M],v[M],tag[M];
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 lower(int x){
  int l=1,r=m,mid,t;
  while(l<=r)if(e[mid=(l+r)>>1]<=x)l=(t=mid)+1;else r=mid-1;
  return t;
}
inline void tag1(int x,int a,int b,int p){
  l[x]=p-e[a];
  v[x]=r[x]=p-e[b];
  tag[x]=p;
}
inline void pb(int x,int a,int b){
  if(tag[x]<0)return;
  int mid=(a+b)>>1;
  tag1(x<<1,a,mid,tag[x]);
  tag1(x<<1|1,mid+1,b,tag[x]);
  tag[x]=-1;
}
inline void up(int x){
  l[x]=l[x<<1];
  r[x]=r[x<<1|1];
  v[x]=min(v[x<<1],v[x<<1|1]);
}
void build(int x,int a,int b){
  tag[x]=-1;
  if(a==b)return;
  int mid=(a+b)>>1;
  build(x<<1,a,mid),build(x<<1|1,mid+1,b);
}
void change(int x,int a,int b,int c,int d,int p){
  if(c<=a&&b<=d){
    if(p-e[a]<=l[x])return;
    if(p-e[b]>r[x]){tag1(x,a,b,p);return;}
  }
  pb(x,a,b);
  int mid=(a+b)>>1;
  if(c<=mid)change(x<<1,a,mid,c,d,p);
  if(d>mid)change(x<<1|1,mid+1,b,c,d,p);
  up(x);
}
int ask(int x,int a,int b,int c,int d){
  if(c<=a&&b<=d)return v[x];
  pb(x,a,b);
  int mid=(a+b)>>1,t=inf;
  if(c<=mid)t=ask(x<<1,a,mid,c,d);
  if(d>mid)t=min(t,ask(x<<1|1,mid+1,b,c,d));
  return up(x),t;
}
int main(){
  read(n);
  for(i=1;i<=n;i++){
    char ch;
    while((ch=getchar())!='C'&&ch!='Q');
    q[i][0]=ch=='C';read(x);
    if(ch=='C'){
      st[i]=m+1,en[i]=m+x;
      while(x--)read(y),b[++m]=y;
    }else q[i][1]=b[++m]=x;
  }
  for(i=1;i<=m;i++)e[i]=b[i];
  sort(e+1,e+m+1);
  build(1,1,m);
  for(i=1;i<=n;i++)if(q[i][0]){
    k=0;
    for(j=st[i];j<=en[i];j++)a[++k]=lower(b[j]);
    sort(a+1,a+k+1);
    for(j=1;j<=k;j++)if(a[j]>a[j-1])change(1,1,m,a[j-1]+1,a[j],e[a[j]]);
    if(a[k]<m)change(1,1,m,a[k]+1,m,inf);
  }else{
    x=ask(1,1,m,lower(q[i][1]),m);
    if(x>1000000000)x=-1;
    printf("%d\n",x);
  }
  return 0;
}

  

posted @ 2016-09-08 09:40  Claris  阅读(206)  评论(0编辑  收藏  举报