hdu 3954(线段树区间更新)

转载标记处:http://www.cnblogs.com/wang-jue/articles/2920341.html


思路:这道题所得到的经验与每个英雄的等级有关,一般的可能就用线段树一直更新到每一个英雄,但这样肯定会超时的。所以我就在想如何使用lazy思想,我发现如果这一段区间内的英雄都已经是最高等级了,那么这一段内肯定是可以用lazy标记的,写完之后TLE了。于是搜到这篇博客,他最开始的思路也是和我一样。这里其实有一个隐含的信息,如果这一段区间内的英雄都没有能够升级的,那么毫无疑问这一段区间内是可以用lazy标记的,如果这一段区间内有英雄要升级的话,那么就更新到叶子节点。这样我们就需要多增加一个域,表示这段区间内如果有英雄要升级,他所需要的最少经验。仔细想想,如果这一段区间没有英雄要升级,那么这段区间内的最大经验值肯定是直接加上最大的等级*倍率即可。所以lazy只需要记录每次输入的倍率即可。这是这道题最难想到的地方。


先来份我的TLE的代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 10005;
struct Segment
{
	int l,r;
	int max_exp,rank;
	int	lazy,cover;
}tree[maxn<<2];
int n,k,m,exprience[15];

void build(int rt,int l,int r)
{
	tree[rt].l = l, tree[rt].r = r;
	tree[rt].rank = 1;
	tree[rt].max_exp = tree[rt].lazy =tree[rt].cover = 0;
	if(l == r) return;
	int mid = (l + r) >> 1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
}

void PushDown(int rt)
{
	if(tree[rt].lazy)
	{
		tree[rt<<1].lazy += tree[rt].lazy;
		tree[rt<<1|1].lazy += tree[rt].lazy;
		tree[rt<<1].max_exp += k * tree[rt].lazy;
		tree[rt<<1|1].max_exp += k * tree[rt].lazy;
		tree[rt].lazy = 0;
	}
}

void update(int rt,int l,int r,int val)
{
	if(tree[rt].l == tree[rt].r)
	{
		tree[rt].max_exp += tree[rt].rank * val;
		if(tree[rt].max_exp >= exprience[tree[rt].rank+1] && tree[rt].rank < k)
			tree[rt].rank++;
		if(tree[rt].rank >= k) 
		{
			tree[rt].cover = 1;
			tree[rt].rank = k;
		}
		return;
	}
	if(tree[rt].cover && l <= tree[rt].l && tree[rt].r <= r)
	{
		tree[rt<<1].cover = tree[rt<<1|1].cover = 1;
		tree[rt].lazy += val;
		tree[rt].max_exp += k * val;
		return;
	}
	PushDown(rt);
	int mid = (tree[rt].l + tree[rt].r) >> 1;
	if(l <= mid) update(rt<<1,l,r,val);
	if(mid < r) update(rt<<1|1,l,r,val);
	if(tree[rt<<1].cover && tree[rt<<1|1].cover)
		tree[rt].cover = 1;
	tree[rt].max_exp = max(tree[rt<<1].max_exp,tree[rt<<1|1].max_exp);
}

int query(int rt,int l,int r)
{
	if(l <= tree[rt].l && tree[rt].r <= r)
		return tree[rt].max_exp;
	PushDown(rt);
	int mid = (tree[rt].l + tree[rt].r) >> 1;
	int ans = 0;
	if(l <= mid) ans = max(ans,query(rt<<1,l,r));
	if(mid < r) ans = max(ans,query(rt<<1|1,l,r));
	return ans;
}

int main()
{
	int t,cas = 1,a,b,c;
	char str[2];
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d%d",&n,&k,&m);
		for(int i = 2; i <= k; i++)
			scanf("%d",&exprience[i]);
		build(1,1,n);
		printf("Case %d:\n",cas++);
		while(m--)
		{
			getchar();
			scanf("%s",str);
			if(str[0] == 'W')
			{
				scanf("%d%d%d",&a,&b,&c);
				update(1,a,b,c);
			}
			else
			{
				scanf("%d%d",&a,&b);
				printf("%d\n",query(1,a,b));
			}
		}
	}
	return 0;
}


这个是看懂了别人的思路打的代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 10005;
struct Segment
{
	int l,r;
	int max_exp,dis_min,rank;
	int	lazy;
}tree[maxn<<2];
int n,k,m,exprience[15];

void build(int rt,int l,int r)
{
	tree[rt].l = l, tree[rt].r = r;
	tree[rt].rank = 1;
	tree[rt].dis_min = exprience[2];
	tree[rt].max_exp = tree[rt].lazy = 0;
	if(l == r) return;
	int mid = (l + r) >> 1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
}

void PushUp(int rt)
{
	tree[rt].max_exp = max(tree[rt<<1].max_exp,tree[rt<<1|1].max_exp);
	tree[rt].dis_min = min(tree[rt<<1].dis_min,tree[rt<<1|1].dis_min);
	tree[rt].rank = max(tree[rt<<1].rank,tree[rt<<1|1].rank);
}

void PushDown(int rt)
{
	if(tree[rt].lazy)
	{
		tree[rt<<1].lazy += tree[rt].lazy;
		tree[rt<<1|1].lazy += tree[rt].lazy;
		tree[rt<<1].max_exp += tree[rt<<1].rank * tree[rt].lazy;
		tree[rt<<1|1].max_exp += tree[rt<<1|1].rank * tree[rt].lazy;
		tree[rt<<1].dis_min -= tree[rt].lazy;
		tree[rt<<1|1].dis_min -= tree[rt].lazy;
		tree[rt].lazy = 0;
	}
}

void update(int rt,int l,int r,int val)
{
	if(l <= tree[rt].l && tree[rt].r <= r)
	{
		if(tree[rt].dis_min > val)	//区间内无英雄升级
		{
			tree[rt].lazy += val;
			tree[rt].max_exp += tree[rt].rank * val;
			tree[rt].dis_min -= val;
			return;
		}
		else if(tree[rt].l == tree[rt].r)
		{
			tree[rt].max_exp += tree[rt].rank * val;
			while(tree[rt].max_exp >= exprience[tree[rt].rank + 1])
				tree[rt].rank++;
			tree[rt].dis_min = (exprience[tree[rt].rank + 1] - tree[rt].max_exp) / tree[rt].rank + ((exprience[tree[rt].rank + 1] - tree[rt].max_exp) % tree[rt].rank != 0);
			return;
		}
	}
	PushDown(rt);
	int mid = (tree[rt].l + tree[rt].r) >> 1;
	if(l <= mid) update(rt<<1,l,r,val);
	if(mid < r) update(rt<<1|1,l,r,val);
	PushUp(rt);
}

int query(int rt,int l,int r)
{
	if(l <= tree[rt].l && tree[rt].r <= r)
		return tree[rt].max_exp;
	PushDown(rt);
	int mid = (tree[rt].l + tree[rt].r) >> 1;
	int ans = 0;
	if(l <= mid) ans = max(ans,query(rt<<1,l,r));
	if(mid < r) ans = max(ans,query(rt<<1|1,l,r));
	return ans;
}

int main()
{
	int t,cas = 1,a,b,c;
	char str[2];
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d%d",&n,&k,&m);
		for(int i = 2; i <= k; i++)
			scanf("%d",&exprience[i]);
		exprience[k+1] = 1 << 30;
		build(1,1,n);
		printf("Case %d:\n",cas++);
		while(m--)
		{
			getchar();
			scanf("%s",str);
			if(str[0] == 'W')
			{
				scanf("%d%d%d",&a,&b,&c);
				update(1,a,b,c);
			}
			else
			{
				scanf("%d%d",&a,&b);
				printf("%d\n",query(1,a,b));
			}
		}
		printf("\n");
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值