hdu3954线段树--4

 http://acm.hdu.edu.cn/showproblem.php?pid=3954

 

题意:初始每个英雄的级别为1,经验为0,M li ri ei表示派li--ri去打怪,每个收获的经验值为当前级别level*ei,Q li ri表示查询li--ri的最大经验。。。

 

分析:很明显的线段树。。。整段更新,整段查询,但整段更新的时候一段里面每个更新的值不一定一样。。大牛说这是中等难度,都不会啊,水。。。

比赛的时候想着对每个节点用个queue保存lazy情况,结果MLE。。。今天又搞了一天啊。。。

后来借鉴了大牛思想。。。http://blog.csdn.net/wsniyufang/article/details/6702560

主要是要利用最大级数不超过10这个条件。。。对每个节点保存这段里面所有的升级所需的最小的值mn。。。然后更新到这一段时如果ei>=mn,则往下更新,最坏情况下,也只有n*klogn这么多。。。不会超时。。。
   
还有一种解决方式是对每个结点开一个10的数组,存该节点每一级的最大值。。。。

ps:原来输入优化也可以优化这么多的。。。。

 

代码:

#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <iostream>
using namespace std;

const int MX=0x7fffffff;
const int N=10010;
int n, k, qw, leve[13], ei;
struct node
{
	int l, r, mid;
	int level, exp, add, mn;
	void init()
	{
		level = 1;
		exp = add = 0;
		mn = leve[2]/1;
	}
} a[N*4];

inline int _max(int a, int b)
{
	return a>b?a:b;
}
inline int _min(int a, int b)
{
	return a<b?a:b;
}
void build(int l, int r, int p)
{
	a[p].l = l;
	a[p].r = r;
	a[p].mid = (l+r)>>1;
	a[p].init();
	if(l==r)
		return;
	build(l, a[p].mid, p*2);
	build(a[p].mid+1, r, p*2+1);
}
//将本层lazy值往下传。。。
inline void down(int p)
{
	a[p*2].add += a[p].add;
	a[p*2].mn -= a[p].add;
	a[p*2].exp += a[p].add*a[p*2].level;

	a[p*2+1].add += a[p].add;
	a[p*2+1].mn -= a[p].add;
	a[p*2+1].exp += a[p].add*a[p*2+1].level;

	a[p].add = 0;
}
//更新过后用下层的结果更新上层。。
inline void up(int p)
{
	a[p].mn = _min(a[p*2].mn, a[p*2+1].mn);
	a[p].exp = _max(a[p*2].exp, a[p*2+1].exp);
	a[p].level = _max(a[p*2].level, a[p*2+1].level);
}

void update(int l, int r, int p)
{
	if(a[p].l==l && r==a[p].r)
	{
		if(l<r)
		{
			if(ei>=a[p].mn)
			{
				down(p);
				update(l, a[p].mid, p*2);
				update(a[p].mid+1, r, p*2+1);
				up(p);
			}
			else
			{
				a[p].add += ei;
				a[p].exp += ei*a[p].level;
				a[p].mn -= ei;
			}
			return;
		}
		a[p].exp += ei*a[p].level;
		for(int i=a[p].level+1; i<=k && a[p].exp>=leve[i]; i++)
			a[p].level = i;
		a[p].mn = (leve[a[p].level+1]-a[p].exp)/a[p].level+((leve[a[p].level+1]-a[p].exp)%a[p].level!=0);//mn的值表示什么。。相当关键。。
		a[p].add = 0;
		return;
	}

	if(a[p].add!=0)
		down(p);
	if(r<=a[p].mid)
		update(l, r, p*2);
	else if(l>a[p].mid)
		update(l, r, p*2+1);
	else
	{
		update(l, a[p].mid, p*2);
		update(a[p].mid+1, r, p*2+1);
	}
	up(p);
}
int query(int l, int r, int p)
{
	if(a[p].l==l && a[p].r==r)
		return a[p].exp;

	if(a[p].add!=0)
		down(p);
	int ans;
	if(r<=a[p].mid)
		ans = query(l, r, p*2);
	else if(l>a[p].mid)
		ans = query(l, r, p*2+1);
	else
	{
		ans = _max(query(l, a[p].mid, p*2), query(a[p].mid+1, r, p*2+1));
	}
	//up(p); //可以不用。因为没有更新,下面不存在会有level变化,上层的值也就不会发生变化。。。
	return ans;
}
//450ms
int main()
{
	int i, cas1, cas, li, ri;
	char op[3];
	//freopen("D.in", "w", stdout);
	scanf("%d", &cas);
	for(cas1=1; cas1<=cas; cas1++)
	{
		scanf("%d%d%d", &n, &k, &qw);
		for(i=2; i<=k; i++)
			scanf("%d", &leve[i]);
		leve[++k] = MX;

		build(1, n, 1);
		printf("Case %d:\n", cas1);
		while(qw--)
		{
			scanf("%s%d%d", op, &li, &ri);
			if(op[0]=='W')
			{
				scanf("%d", &ei);
				update(li, ri, 1);
			}
			else
			{
				printf("%d\n", query(li, ri, 1));
			}
		}
		printf("\n");
	}

	return 0;
}
/*
//输入优化。。。
//加上这个输入优化之后居然就可以榜首了。。343MS。。时间差不多少了3/4
inline int nextInt() {    
    char c;    
    while (c = getchar(), c < '0' || c > '9');    
    int r = c - '0';    
    while (c = getchar(), c >= '0' && c <= '9') r = r * 10 + c - '0';    
    return r;    
} 
int main()
{
	int i, j, cas1, cas, li, ri;
	char op[3];
	//freopen("D.in", "w", stdout);
	cas = nextInt();
	for(cas1=1; cas1<=cas; cas1++)
	{
		n=nextInt(); k=nextInt(); qw=nextInt();
		for(i=2; i<=k; i++)
			leve[i] = nextInt();
		leve[++k] = MX;

		build(1, n, 1);
		printf("Case %d:\n", cas1);
		while(qw--)
		{
			scanf("%s", op);
			li = nextInt();
			ri = nextInt();
			if(op[0]=='W')
			{
				//scanf("%d", &ei);
				ei = nextInt();
				update(li, ri, 1);
			}
			else
			{
				printf("%d\n", query(li, ri, 1));
			}
		}
		puts("");
	}
	return 0;
} 
*/


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值