题目描述
在一个塔防小游戏中,有很多防线。 每条防线由一排n个独立的防御体[1 : n]进行防御。
游戏过程中,会不断有敌人对防线进行攻击,每次攻击会指定防御体[l : r]进行攻击力为a的攻击。 第一防线具有护甲,护甲承受攻击后,对应的防御体所受到的伤害为攻击力,但护甲承受的伤害总量到达一定程度后就会破碎,此时防御体所受的伤害加倍。 目前第一防线的力量充足,玩家致力于对后面的防线的建设,不过为确认游戏进度和第一防线的情况,玩家会不时地将鼠标移动到第一防线的某个防御体上,以查看其所受到的伤害。
输入输出格式
输入格式:
第一行,两个整数n; q,分别表示防御体个数以及攻击和查询的总数。
第二行, n个数,表示每个防御体护甲的承受能力pi。
接下来 q 行,每行可能具有如下形式
A l r a 表示对防御体l,l+1,.....,r进行攻击力为a的攻击
Q x 表示查询防御体x目前所受到的伤害,初始时伤害为0
输出格式:
一行,一个整数,表示所有查询结果之和对1,000,000,009的余数
输入输出样例
输入样例#1:
5 7 3 1 4 1 2 A 1 3 2 Q 2 A 1 4 1 Q 1 A 1 4 1 Q 2 Q 1
输出样例#1:
16
说明
样例解释
3/0 1/0 4/0 1/0 2/0
[A 1 3 2]
1/2 2 2/2 1/0 2/0
[Q 2] ! 2
[A 1 4 1]
3 4 1/3 1 2/0
[Q 1] -> 3
[A 1 4 1]
5 6 4 3 2/0
[Q 2] -> 6
[Q 1] -> 5
数据范围
30%的数据, q ≤ 1000
100%的数据, 1 ≤ n ≤ 100000, 1 ≤ q ≤ 100000, 0 ≤ pi ≤ 1000000, 0 ≤ a ≤ 10000
这一题不是很难,但是十分恶心
比如说一个位置的盾只剩下1,然后造成99999的伤害,题目的意思是然盾爆掉,然后物体受到99999的伤害,没有翻倍
一开始我是想用树状数组+差分来做的,但是发现上面这种情况无法处理,于是就只能用线段树了
因为是区间造成伤害,所以需要用到lazy标记
为了知道在一次造成伤害以后是否会有物体的盾爆掉,所以我打了一个minp[rt]数组来表示rt的子树中护甲剩下最小的叶子节点
如果一次攻击打爆了护盾,就把爆护盾的叶子节点记录一下本次的伤害,并且多打一个函数来维护,每一个节点logn,因为盾最多爆一次,所以总维护时间为O(nlogn)
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std ;
typedef long long LL ;
const int N = 1e5 + 10 ;
const int mod = 1e9 + 9 ;
inline int read() {
int x = 0 , f = 0 ; char s = getchar() ;
while ( !isdigit(s) ) f |= s=='-' , s = getchar() ;
while ( isdigit(s) ) x = (x<<1) + (x<<3) + s - 48 , s = getchar() ;
return !f ? x : -x ;
}
int n , q ;
LL p[N] , minp[N<<2] , sum[N<<2] , lazy[N<<2] ;
bool boom[N] ;
//p表示节点的护甲
//minp表示当前节点子树中剩余护甲最小的叶子节点,并且用来削护甲
//sum表示所受伤害
//lazy表示延迟修改
//boom表示节点的盾是否存在
void update1( int rt ) { //维护minp
minp[rt] = min ( minp[rt<<1] , minp[rt<<1|1] ) ;
}
void update2( int rt ) { //维护lazy
if ( minp[rt<<1] != 2e9 ) minp[rt<<1] -= lazy[rt] ; //巧妙的减去护甲,维护minp
if ( minp[rt<<1|1] != 2e9 ) minp[rt<<1|1] -= lazy[rt] ;
lazy[rt<<1] += lazy[rt] , lazy[rt<<1|1] += lazy[rt] ; // 将lazy往下遗传
lazy[rt] = 0 ;
}
void build( int l , int r , int rt ) { //建树
if( l == r ) {
p[l] = read() ;
minp[rt] = p[l] ? p[l] : 2e9 ;
return ;
}
int mid = (l+r) >> 1 ;
build ( l , mid , rt<<1 ) , build ( mid+1 , r , rt<<1|1 ) ;
update1 ( rt ) ;
}
void change( int l , int r , int rt , int c ) { //如果有护盾在一次攻击中被打爆了,就用change来维护一下
if ( l == r ) { p[l] = p[l] - minp[rt] + c ; minp[rt] = 2e9 ; boom[l] = 1 ; return ; }
int mid = ( l + r ) >> 1 ;
if ( lazy[rt] != 0 ) update2( rt ) ;
if ( minp[rt<<1] <= c ) change( l , mid , rt<<1 , c ) ;
if ( minp[rt<<1|1] <= c ) change( mid + 1 , r , rt<<1|1 , c ) ;
update1( rt ) ; //记得维护
}
void add ( int l , int r , int rt , int L , int R , int c , LL s ) {
if ( L <= l && r <= R ) { //如果到达了目标区域
sum[rt] += c ; //增加伤害
if ( minp[rt] <= c ) change( l , r , rt , c ) ; //如果有盾爆掉
if ( minp[rt] != 2e9 ) minp[rt] -= c , lazy[rt] += c ;
return ;
}
s += sum[rt] ;
if ( lazy[rt] != 0 ) update2( rt ) ;
int mid = ( l + r ) >> 1 ;
if ( L <= mid ) add ( l , mid , rt<<1 , L , R , c , s ) ;
if ( mid < R ) add ( mid+1 , r , rt<<1|1 , L , R , c , s ) ;
update1( rt ) ;
}
int query( int l , int r , int rt , int p ) { //查询函数
if ( l == r ) return sum[rt] % mod ;
int mid = ( l + r ) >> 1 ;
if ( p <= mid ) return ( sum[rt] + query( l , mid , rt<<1 , p ) ) % mod ;
else return ( sum[rt] + query ( mid+1 , r , rt<<1|1 , p ) ) % mod ;
}
int main() {
n = read() , q = read() ; LL ans = 0;
build ( 1 , n , 1 ) ;
while ( q -- ) {
scanf("\n") ;
if ( getchar() == 'A' ) {
int L = read() , R = read() , C = read() ;
add( 1 , n , 1 , L , R , C , 0 ) ;
}
else {
int x = read() ;
if ( boom[x] ) ans = ( ans + query( 1 , n , 1 , x ) * 2 - p[x] + mod ) % mod ;
else ans = ( ans + query( 1 , n , 1 , x ) + mod ) % mod ;
}
}
printf ( "%lld\n" , ans ) ;
return 0 ;
}
其实这一个代码有几个地方很难理解,欢迎大家在博客下方留言