[难]hihocoder 线段树 毁灭者问题

如果对线段树还不熟悉的,请点这里了解基本概念与编码实现。

 

题目2 : 毁灭者问题

时间限制: 10000ms
单点时限: 1000ms
内存限制: 256MB

描述

在 Warcraft III 之冰封王座中,毁灭者是不死族打三本后期时的一个魔法飞行单位。

毁灭者的核心技能之一,叫做魔法吸收(Absorb Mana):

14054064004625.png

现在让我们来考虑下面的问题:

假设你拥有 n 个魔法单位,他们从左到有站在一行,编号从 1 到 n。 每个单位拥有三项属性:


  • si: 初始法力。

  • mi: 最大法力上限。

  • ri: 每秒中法力回复速度。


现在你操纵一个毁灭者,有 m 个操作,t l r,表示时刻 t,毁灭者对所有编号从lr 的单位,使用了魔法吸收。操作按照时间顺序给出,计算毁灭者一共吸收了多少法力。


输入

输入数据的第一行有一个整数 n(1 ≤  n105) — 你的魔法单位的数目。

接下来的 n 行,每行有三个整数 si, mi, ri(0 ≤ si ≤ mi ≤ 105,0 ≤ ri ≤ 105) 描述一个魔法单位。

接下来一行又一个整数 m(1 ≤ m ≤ 105), — 操作的数目。

接下来的 m 行,每行描述一个操作 t, l, r(0 ≤ t ≤ 109,1 ≤ l ≤ r ≤ n),t 非降。


输出

输出一行一个整数表示毁灭者一共吸收了多少法力。

样例输入
5
0 10 1
0 12 1
0 20 1
0 12 1
0 10 1
2
5 1 5
19 1 5
样例输出
83
很明显是一个用线段树的题,每个节点中需要保存信息有:s初始法力,m最大法力上限,r每秒法力回复速度。由于每个单位的恢复值都不一样,所以要开不同的数组存放这些信息。首先建立整个线段树,把里面所有信息初始化,从区间[l,r]中进行查询,注意sum数组和其他数组之间并不是单纯的一对一联系,所以每次操作后要进行对应,即执行pushpu函数。
    另外因为有输入时间T作参数,数据的及时更新也是不可缺少的。
    一些小细节注意。及时清空,计算时候一些对时间的优化做好了就可以了。
 
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int sum[100010<<2];
int m[100000],ri[100000],t[100010<<2];
const long long MOD=1000000007;
void pushup(int root)
{
	sum[root]=sum[root<<1]+sum[root<<1|1];
}
void build(int l,int r,int root)
{
	if(r==l){
		scanf("%d%d%d",&sum[root],&m[l],&ri[l]);
		t[l]=0;
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,root<<1);
	build(mid+1,r,root<<1|1);
	pushup(root);
}
void recover(int time,int L,int R,int l,int r,int root)
{
	if(l==r&&(l>=L&&l<=R)){
		int temp=(time-t[l])*ri[l];
		if(sum[root]+temp>m[l]) sum[root]=m[l];
		else sum[root]+=temp;
		return ;
	}
	int mid=(l+r)>>1;
	if(L<=mid) recover(time,L,R,l,mid,root<<1);
	if(R>mid) recover(time,L,R,mid+1,r,root<<1|1);
	pushup(root);
}
long long query(int L,int R,int l,int r,int root)
{
	if(L<=l&&R>=r) return sum[root];
	int mid=(l+r)>>1;
	long long ans=0;
	if(L<=mid) ans=query(L,R,l,mid,root<<1);
	if(R>mid) ans+=query(L,R,mid+1,r,root<<1|1);
	return ans;
}
void clea(int time,int L,int R,int l,int r,int root)
{
	if(l==r&&(l>=L&&l<=R)){
		t[l]=time;
		sum[root]=0;
		return ;
	}
	int mid=(l+r)>>1;
	if(L<=mid) clea(time,L,R,l,mid,root<<1);
	if(R>mid) clea(time,L,R,mid+1,r,root<<1|1);
	pushup(root);
}
int main()
{
	int n;
	memset(t,0,sizeof(t));
	scanf("%d",&n);
	build(1,n,1);
	int tm;
	scanf("%d",&tm);
	long long ans=0;
	for(int i=1;i<=tm;i++){
		int time,l,r;
		scanf("%d%d%d",&time,&l,&r);
		recover(time,l,r,1,n,1);
		ans=(ans+query(l,r,1,n,1))%MOD;
		clea(time,l,r,1,n,1);
	}
	printf("%lld",ans);
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值