[ 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;
}