[TJOI2012]防御

题目描述

在一个塔防小游戏中,有很多防线。 每条防线由一排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 ;
}

其实这一个代码有几个地方很难理解,欢迎大家在博客下方留言

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值