[ TJOI 2012 ] 防御

\(\\\)

Description


\(n\) 人,第 \(i\) 个人有一个护甲值 \(a_i\)

\(m\) 次操作,分为以下两种:

  • \(A\ l\ r\ x\) 对编号在 \([l,r]\) 内的人造成 \(x\) 点伤害。
  • \(Q\ p\) 求第 \(p\) 个人当前受到的伤害值之和。

护甲值的作用是,如果当前总伤害未超过护甲值,只对这个人造成一倍伤害。

当总伤害第一次 \(\ge\) 护甲值时护甲破了,以后 的攻击都造成二倍伤害,破甲时的攻击 只造成一倍伤害

  • \(n,m\le 10^5,x\le 10^4,a_i\le 10^6\)

\(\\\)

Solution


我们记 \(val_i\) 表示第 \(i\) 个人被破甲时累积的伤害值是多少,答案就是第 \(i\) 个人受到的总伤害值 \(\times 2-val_i\)

然后考虑如何求 \(val_i\) 。线段树 \(DFS\)

线段树维护当前节点收到的伤害之和,以及当前区间最小 剩余 护甲值。

每次攻击打 \(lazy\ tag\) ,同时判断区间最小值是否 \(\le 0\)

如果出现最小值 \(\le 0\) 的情况,就从当前节点 开始向下 \(DFS\) , $pushdown $ 之后,分别判断左右子树,只要子树最小就继续 \(DFS\) 这个子树。到了叶节点就可以统计对应位置的 \(val\) 值了。

记得修改之后及时 \(pushup\) ,为了不影响后续的判断,我们把更新过的叶节点剩余护甲值设为 \(inf\)

并不需要循环,因为每次攻击时左右子节点至多被 \(DFS\) 一次就可以处理掉所有不合法情况。

因为每个节点至多被\(DFS\) 进去一次,所以复杂度还是 \(n\ log\ n\) 的。

以及 \(DFS\) 之前要先判断最小值是否 \(\le 0\),否则可能会出现多次进叶节点的情况。

\(\\\)

Code


#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N  100010
#define gc getchar
#define Rg register
#define mid ((l+r)>>1)
#define inf 1000000000
#define mod 1000000009
using namespace std;

inline int rd(){
  int x=0; bool f=0; char c=gc();
  while(!isdigit(c)){if(c=='-')f=1;c=gc();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
  return f?-x:x;
}

int n,m,lim[N],val[N];

struct segment{

  int root,ptr;

  inline int newnode(){return ++ptr;}

  struct node{int ls,rs,mn,tag;}c[N<<1];

  inline void pushup(int rt){
    c[rt].mn=min(c[c[rt].ls].mn,c[c[rt].rs].mn);
  }

  inline void pushdown(int rt){
    if(c[rt].tag){
      c[c[rt].ls].tag+=c[rt].tag;
      c[c[rt].ls].mn-=c[rt].tag;
      c[c[rt].rs].tag+=c[rt].tag;
      c[c[rt].rs].mn-=c[rt].tag;
      c[rt].tag=0;
    }
  }

  void build(int &rt,int l,int r){
    rt=newnode();
    if(l==r){
      c[rt].mn=lim[l];
      return;
    }
    build(c[rt].ls,l,mid);
    build(c[rt].rs,mid+1,r);
    pushup(rt);
  }

  void check(int rt,int l,int r){
    if(l==r){
      val[l]=lim[l]-c[rt].mn;
      c[rt].mn=inf;
      return;
    }
    pushdown(rt);
    if(c[c[rt].ls].mn<=0) check(c[rt].ls,l,mid);
    if(c[c[rt].rs].mn<=0) check(c[rt].rs,mid+1,r);
    pushup(rt);
  }

  void updata(int rt,int l,int r,int L,int R,int w){
    if(l>R||r<L) return;
    if(l>=L&&r<=R){
      c[rt].tag+=w;
      c[rt].mn-=w;
      if(c[rt].mn<=0) check(rt,l,r);
      return;
    }
    pushdown(rt);
    if(L<=mid) updata(c[rt].ls,l,mid,L,R,w);
    if(R>mid) updata(c[rt].rs,mid+1,r,L,R,w);
    pushup(rt);
  }

  int query(int rt,int l,int r,int p){
    if(p>r||p<l) return 0;
    if(l==r) return c[rt].tag;
    pushdown(rt);
    if(p<=mid) return query(c[rt].ls,l,mid,p)+c[rt].tag;
    else return query(c[rt].rs,mid+1,r,p)+c[rt].tag;
  }

}tree;

int res;

int main(){
  n=rd(); m=rd();
  for(Rg int i=1;i<=n;++i) lim[i]=rd();
  tree.build(tree.root,1,n);
  char c;
  for(Rg int i=1,x,y,w;i<=m;++i){
    c=gc(); while(!isalpha(c)) c=gc();
    if(c=='Q'){
      x=rd();
      if(val[x]) (res+=(tree.query(tree.root,1,n,x)*2-val[x])%mod)%=mod;
      else (res+=tree.query(tree.root,1,n,x)%mod)%=mod;
    }
    else{
      x=rd(); y=rd(); w=rd();
      tree.updata(tree.root,1,n,x,y,w);
    }
  }
  printf("%d\n",res);
  return 0;
}

posted @ 2018-11-12 21:16  SGCollin  阅读(134)  评论(0编辑  收藏  举报