POJ 3468 A Simple Problem with Integers (线段树)

30 篇文章 0 订阅

第一次做线段树的题目,之前看过一点线段树的内容,自以为懂了,结果一提交TLE后来上网看了知道了延迟更新,终于1600msA了

顺便说一下延迟更新问题,如果我们仔细观察线段树的更新操作,其实更新是o(L*lgN)级别的因为要更新到叶子为止

但是其实更新并不需要更新到叶子节点,只需要搜索到等大的区间时在此区间上做个标记即可,复杂度为o(lgN),比如更新[1,5]这个区间,只需要在[1,5]区间上(如果分成n个区间段就在各个区间段)上添加一个add_to_all的段,把值加到这个段上即可。

查的时候将add_to_all的值不断增加下推即可

给出代码


#include <stdio.h>

struct node
{
	int right;
	int left;
	long long sum;
	long long tototal;
}tree[1000000];

void init(int left,int right,int now,long long value,int des)
{
	int middle=(left+right)>>1;
	tree[now].left=left;
	tree[now].right=right;
	tree[now].sum+=value;
	if (left != right)
	{
		if(des>middle)
		{
			init(middle+1,right,(now<<1)+2,value,des);
		}
		else
		{
			init(left,middle,(now<<1)+1,value,des);
		}
	}
	return;
}

long long sum(int left,int right,int now)
{
	int middle=(tree[now].left+tree[now].right)>>1;
	if(left != tree[now].left || right != tree[now].right)
	{
		if(left > middle)
		{
			return sum(left,right,(now<<1)+2)+tree[now].tototal*(right-left+1);
		}
		else
		{
			if(right <= middle)
			{
				return sum(left,right,(now<<1)+1)+tree[now].tototal*(right-left+1);
			}
			else
			{
				return sum(left,middle,(now<<1)+1)+sum(middle+1,right,(now<<1)+2)+tree[now].tototal*(right-left+1);
			}
		}
	}
	else
	{
		return tree[now].sum+tree[now].tototal*(right-left+1);
	}
}

void add(int left,int right,int now,int addvalue)
{
	int middle=(tree[now].left+tree[now].right)>>1;
	if(left == tree[now].left && right == tree[now].right)
	{
		tree[now].tototal+=addvalue;
		return;
	}
	tree[now].sum+=addvalue*(right-left+1);
	if(tree[now].left != tree[now].right)
	{
		if(left > middle)
		{
			add(left,right,(now<<1)+2,addvalue);
		}
		else
		{
			if(right <= middle)
			{
				add(left,right,(now<<1)+1,addvalue);
			}
			else
			{
				add(left,middle,(now<<1)+1,addvalue);
				add(middle+1,right,(now<<1)+2,addvalue);
			}
		}
	}
	return;
}
int main()
{
	int N,Q,i,a,b,addvalue;
	long long value;
	char tmp;
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	scanf("%d %d",&N,&Q);
	for(i=0;i<N;++i)
	{
		scanf("%I64d",&value);
		init(0,N-1,0,value,i);
	}
	for(i=0;i<Q;++i)
	{
		getchar();
		scanf("%c %d %d",&tmp,&a,&b);
		if('Q' == tmp)
		{
			printf("%I64d\n",sum(a-1,b-1,0));
		}
		else
		{
			scanf("%d",&addvalue);
			add(a-1,b-1,0,addvalue);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值