BZOJ5326 : [Jsoi2017]博弈

Claris發表於2019-02-11

將所有物品按照$b$的選擇順序排序,則先手在任意前$i$個物品中最多隻能拿走$\lceil\frac{i}{2}\rceil$個物品。

將每個物品的價值設為$a+b$,那麼答案為先手拿走的價值和減去所有物品的$b$之和,目標是最大化先手拿走的價值和。

如果不考慮修改,則滿足擬陣,可以貪心選取,修改時最優解最多發生一處變動。

每個修改可以看作是先刪除一個物品,再插入一個物品。

設$f[i]$表示$[1,i]$裡選擇的物品數減去$\lceil\frac{i}{2}\rceil$,則方案合法的條件為$\max(f[1,n])\leq 0$。

 

對於插入物品$x$:

  • 如果$x$可以直接加入最優解,那麼直接加入。
  • 否則加入$x$會導致$f$中出現$1$,那麼需要找到一個最優解中的價值最小的物品$y$,滿足$y$在$f$從左往右第一個$1$之前,要麼不選$x$,要麼將$y$替換成$x$。

 

對於刪除物品$x$:

  • 如果$x$不在最優解中,那麼直接刪除。
  • 否則刪除$x$後可以繼續多加入一個物品,那麼需要找到一個不在最優解中的價值最大的物品$y$,滿足$y$到$n$的所有$f$都是負數。

 

以上所有操作都可以用線段樹維護,時間複雜度$O((n+m)\log n)$。

 

#include<cstdio>
#include<algorithm>
using namespace std;
typedef pair<int,int>P;
const int N=100010,M=262150,inf=~0U>>1,BUF=10000000;
char Buf[BUF],*buf=Buf;long long ans;
int n,m,i,x,y,pos[N];bool used[N];
P ma[M],mi[M];int f[M],tag[M];
struct E{int a,b,p;}e[N];
inline bool cmp(const E&a,const E&b){
  if(a.b!=b.b)return a.b>b.b;
  return a.p<b.p;
}
inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;}
inline void up(int x){
  ma[x]=max(ma[x<<1],ma[x<<1|1]);
  mi[x]=min(mi[x<<1],mi[x<<1|1]);
}
void build(int x,int a,int b){
  if(a==b){
    ma[x]=P(-inf,a);
    mi[x]=P(inf,a);
    f[x]=-(a+1)/2;
    return;
  }
  int mid=(a+b)>>1;
  build(x<<1,a,mid),build(x<<1|1,mid+1,b),up(x);
  f[x]=max(f[x<<1],f[x<<1|1]);
}
inline void tag1(int x,int p){f[x]+=p;tag[x]+=p;}
inline void pb(int x){if(tag[x])tag1(x<<1,tag[x]),tag1(x<<1|1,tag[x]),tag[x]=0;}
void maketag(int x,int a,int b,int c,int d,int p){
  if(c<=a&&b<=d){tag1(x,p);return;}
  pb(x);
  int mid=(a+b)>>1;
  if(c<=mid)maketag(x<<1,a,mid,c,d,p);
  if(d>mid)maketag(x<<1|1,mid+1,b,c,d,p);
  f[x]=max(f[x<<1],f[x<<1|1]);
}
void setused(int x,int a,int b,int c,int p){
  if(a==b){
    used[a]=1;
    ma[x]=P(-inf,a);
    mi[x]=P(p,a);
    return;
  }
  int mid=(a+b)>>1;
  if(c<=mid)setused(x<<1,a,mid,c,p);else setused(x<<1|1,mid+1,b,c,p);
  up(x);
}
void setunused(int x,int a,int b,int c,int p){
  if(a==b){
    used[a]=0;
    ma[x]=P(p,a);
    mi[x]=P(inf,a);
    return;
  }
  int mid=(a+b)>>1;
  if(c<=mid)setunused(x<<1,a,mid,c,p);else setunused(x<<1|1,mid+1,b,c,p);
  up(x);
}
int askf(int x,int a,int b,int c,int d){
  if(c<=a&&b<=d)return f[x];
  pb(x);
  int mid=(a+b)>>1,t=-inf;
  if(c<=mid)t=askf(x<<1,a,mid,c,d);
  if(d>mid)t=max(t,askf(x<<1|1,mid+1,b,c,d));
  return t;
}
P askmi(int x,int a,int b,int c,int d){
  if(c<=a&&b<=d)return mi[x];
  int mid=(a+b)>>1;P t(inf,0);
  if(c<=mid)t=askmi(x<<1,a,mid,c,d);
  if(d>mid)t=min(t,askmi(x<<1|1,mid+1,b,c,d));
  return t;
}
P askma(int x,int a,int b,int c,int d){
  if(c<=a&&b<=d)return ma[x];
  int mid=(a+b)>>1;P t(-inf,0);
  if(c<=mid)t=askma(x<<1,a,mid,c,d);
  if(d>mid)t=max(t,askma(x<<1|1,mid+1,b,c,d));
  return t;
}
inline int findfirstpos(){
  int x=1,a=1,b=n,mid;
  while(a<b){
    pb(x);
    mid=(a+b)>>1;
    if(f[x<<1]>0)x=x<<1,b=mid;else x=x<<1|1,a=mid+1;
  }
  return a;
}
inline int findlastneg(){
  int x=1,a=1,b=n,mid,ret=n+1;
  while(a<b){
    pb(x);
    mid=(a+b)>>1;
    if(f[x<<1|1]<0){
      x=x<<1;
      b=mid;
      ret=mid+1;
    }else{
      x=x<<1|1;
      a=mid+1;
    }
  }
  if(f[x]<0)ret=a;
  return ret;
}
inline void additem(int x){
  int val=e[x].a+e[x].b,tmp=askf(1,1,n,x,n);
  maketag(1,1,n,x,n,1);
  if(tmp<0){
    setused(1,1,n,x,val);
    ans+=val;
    return;
  }
  int o=findfirstpos();
  maketag(1,1,n,x,n,-1);
  P y=askmi(1,1,n,1,o);
  if(y.first>=val){
    setunused(1,1,n,x,val);
    return;
  }
  ans+=val-y.first;
  setunused(1,1,n,y.second,y.first);
  maketag(1,1,n,y.second,n,-1);
  setused(1,1,n,x,val);
  maketag(1,1,n,x,n,1);
}
inline void delitem(int x){
  if(!used[x])return;
  ans-=e[x].a+e[x].b;
  setunused(1,1,n,x,-inf);
  maketag(1,1,n,x,n,-1);
  int o=findlastneg();
  if(o>n)return;
  P y=askma(1,1,n,o,n);
  if(y.first<=0)return;
  ans+=y.first;
  setused(1,1,n,y.second,y.first);
  maketag(1,1,n,y.second,n,1);
}
int main(){
  fread(Buf,1,BUF,stdin);read(n);
  for(i=1;i<=n;i++)read(e[i].a);
  for(i=1;i<=n;i++)read(e[i].b),e[i].p=i,ans-=e[i].b;
  sort(e+1,e+n+1,cmp);
  for(i=1;i<=n;i++)pos[e[i].p]=i;
  build(1,1,n);
  for(i=1;i<=n;i++)additem(i);
  printf("%lld\n",ans);
  read(m);
  while(m--){
    read(x),read(y);
    x=pos[x];
    delitem(x);
    e[x].a=y;
    additem(x);
    printf("%lld\n",ans);
  }
  return 0;
}

  

相關文章