[BZOJ2130][均摊复杂度线段树]魔塔

%%%Hillan

具体题解可以去看Claris的博客
暴力线段树复杂度摊一摊就nlogn了

#include <cstdio>
#include <algorithm>
#include <iostream>
#define N 100010

using namespace std;

int t,n,r;
int A[N],B[N],C[N],K[N],K1[N],f[N],ca[N],cb[N],cc[N],kb[N],kc[N],pb[N],pc[N];
struct seg{
  int l,r,Maxc,Minc,Max,flg,Maxb;
  bool used;
}T[N<<2];

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline void reaD(int &x){
  char c=nc(); x=0;
  for(;c>'9'||c<'0';c=nc());
  for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}

inline void Update(int g){
  int ls=g<<1,rs=g<<1|1;
  T[g].Maxb=max(T[ls].used?T[ls].Maxb:-1,T[rs].used?T[rs].Maxb:-1);
  T[g].Maxc=T[ls].used?T[ls].Maxc:T[rs].Maxc;
  T[g].Minc=T[rs].used?T[rs].Minc:T[ls].Minc;
  T[g].Max=max(T[ls].used?T[ls].Max:-1,T[rs].used?T[rs].Max:-1);
  T[g].used=T[ls].used|T[rs].used;
}

void Build(int g,int l,int r){
  T[g].l=l; T[g].r=r;
  T[g].flg=-1; T[g].used=1;
  int mid=l+r>>1;
  if(l==r)
    T[g].Maxc=T[g].Minc=f[l],T[g].Max=f[l]+pb[l],T[g].Maxb=pb[l];
  else
    Build(g<<1,l,mid),Build(g<<1|1,mid+1,r),Update(g); 
}

inline void toMin(int g,int x){
  if(!T[g].used) return ;
  T[g].Maxc=T[g].Minc=x;
  T[g].Max=T[g].Maxb+T[g].Maxc;
  T[g].flg=x;
}

void Pushdown(int g){
  if(!T[g].used) return ;
  if(~T[g].flg)
    toMin(g<<1,T[g].flg),toMin(g<<1|1,T[g].flg),T[g].flg=-1;
}

void Erase(int g,int l,int r){
  if(!T[g].used) return ;
  if(T[g].l==l&&T[g].r==r) return (void)(T[g].used=0);
  Pushdown(g);
  int mid=T[g].l+T[g].r>>1;
  if(r<=mid) Erase(g<<1,l,r);
  else if(l>mid) Erase(g<<1|1,l,r);
  else Erase(g<<1,l,mid),Erase(g<<1|1,mid+1,r);
  Update(g);
}

void Min(int g,int l,int r,int x){
  if(T[g].used==0||T[g].Maxc<=x) return ;
  if(T[g].l==l&&T[g].r==r&&T[g].Minc>=x)
    return toMin(g,x);
  Pushdown(g);
  int mid=T[g].l+T[g].r>>1;
  if(r<=mid) Min(g<<1,l,r,x);
  else if(l>mid) Min(g<<1|1,l,r,x);
  else Min(g<<1,l,mid,x),Min(g<<1|1,mid+1,r,x);
  Update(g);
}

int main(){
  freopen("Mota.in","r",stdin);
  freopen("Mota.out","w",stdout);
  reaD(t); 
  while(t--){
    reaD(n); int ret=0,Ans=0,pre=0;
    for(int i=1;i<=n;i++) reaD(K[i]),K1[i]=K[i];
    for(int i=1;i<=n;i++) reaD(A[i]);
    for(int i=1;i<=n;i++) reaD(B[i]),kb[B[i]]=i;
    for(int i=1;i<=n;i++) reaD(C[i]),kc[C[i]]=i;
    for(int i=1;i<=n;i++) reaD(ca[i]);
    for(int i=1;i<=n;i++) reaD(cb[i]),pb[i]=pb[i-1]+cb[i];
    for(int i=1;i<=n;i++) reaD(cc[i]),ret+=cc[i],pc[i]=pc[i-1]+cc[i];
    f[0]=ret;
    for(int i=1,j=n;i<=n;i++){
      K1[B[i]]--;
      if(!K1[B[i]]) j=min(j,kc[B[i]]-1);
      f[i]=pc[j];
    } 
    Build(1,0,n); Ans=T[1].Max;
    for(int i=1;i<=n;i++){
      K[A[i]]--; pre+=ca[i];
      if(!K[A[i]])
    Erase(1,kb[A[i]],n),Min(1,0,kb[A[i]]-1,pc[kc[A[i]]-1]);
      else Min(1,kb[A[i]],n,pc[kc[A[i]]-1]);
      Ans=max(Ans,pre+T[1].Max);
    }
    printf("%d\n",Ans);
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值