线段树(1)之单点更新(基础题)

  在代码前先介绍一些我的线段树风格
  • MAX是题目给的最大区间,而节点数要开4倍,确切的来说节点数要开大于MAX的最小2x的两倍
  • l和r分别表示某个区间的最左与最右的点,由于每次传参数的时候都固定是这几个变量,所以可以用预定于比较方便的表示,mid表示区间中间的(我比较喜欢定义一个mid,也可以不用定义,只是每次要去算mid=(l+r)/2)
  • 点以前的写法是另外开两个个数组记录每个结点所表示的区间,其实这个区间不必保存,一边算一边传下去就行,只需要写函数的时候多两个参数,结合l和r的预定义可以很方便
  • PushUP(int i)是把当前结点的信息更新到父结点,及向上更新
  • PushDown(int i)是把当前结点的信息更新给儿子结点及向下更新
  • i就是当前所在的结点。                                                 
  • (1)单点更新:最最基础的线段树,只更新叶子节点,然后把信息用PushUP(int i)这个函数更新上来:废话不多说,直接上干粮,这道题是我做的第一道线段树:(hdu 1166)

Description

Lily 特别喜欢养花,但是由于她的花特别多,所以照料这些花就变得不太容易。她把她的花依次排成一行,每盆花都有一个美观值。如果Lily把某盆花照料的好的话,这盆花的美观值就会上升,如果照料的不好的话,这盆花的美观值就会下降。有时,Lily想知道某段连续的花的美观值之和是多少,但是,Lily的算术不是很好,你能快速地告诉她结果吗?

Input

	第一行一个整数T,表示有T组测试数据。
每组测试数据的第一行为一个正整数N(N<=50000),表示Lily有N盆花。接下来有N个正整数,第i个正整数ai表示第i盆花的初始美观值(1<=ai<=50)。
接下来每行有一条命令,命令有4种形式:
(1)Add i j, i和j为正整数,表示第i盆花被照料的好,美观值增加j(j<=30)
(2)Sub i j, i和j为正整数,表示第i盆花被照料的不好,美观值减少j(j<=30)
(3)Query i j, i和j为正整数,i<=j,表示询问第i盆花到第j盆花的美观值之和
(4)End,表示结束,这条命令在每组数据最后出现
每组数据的命令不超过40000条
 
   

Output

	对于第i组数据,首先输出"Case i:"和回车。
对于每个"Query i j"命令,输出第i盆花到第j盆花的美观值之和。

Sample Input

1
9
7 9 8 4 4 5 4 2 7
Query 7 9
Add 4 9
Query 3 6
Sub 9 6
Sub 3 3
Query 1 9
End
 
   

Sample Output

Case 1:
133050
    看了前面一页博客后,看到题中40000和50000这两个数据之后,果断线段树,原理就是上一页博客说的最基本的点更新原理,每个结构体数组要存某个区间的花的美观值之和,晓得这个之后,就是套路了,直接上代码:
#include<stdio.h>
#include<string.h>
#define MAX 50000
struct node
{
	int l,r;
	int mid;
	int sum;
};
node a[MAX*4];
void BuildTree(int i,int l,int r)     //建立线段树
{
	a[i].l=l;
	a[i].r=r;
	if(l==r)
	{
		scanf("%d",&a[i].sum);
		return;
	}
	a[i].mid=(l+r)/2;
	BuildTree(i*2,l,a[i].mid);
	BuildTree(i*2+1,a[i].mid+1,r);
	a[i].sum=a[i*2].sum+a[i*2+1].sum;    //这其实就是我说的那个PushUp(向上更新),这题较简单,向上更新只需要一个语句即可实现,所以就没再写个PushUp函数了
}
void update(int i,int index,int num)   //点更新
{
	if(a[i].l==a[i].r)
	{
		a[i].sum+=num;
		return;
	}
	if(a[i].mid>=index)update(i*2,index,num);
	if(a[i].mid<index)update(i*2+1,index,num);
	a[i].sum=a[i*2].sum+a[i*2+1].sum;
}
int Query(int i,int l,int r)      //区间询问
{
	if(a[i].l==l&&a[i].r==r)return a[i].sum;
	if(r<=a[i].mid)return Query(i*2,l,r);
	if(l>a[i].mid)return Query(i*2+1,l,r);
	else return Query(i*2,l,a[i].mid)+Query(i*2+1,a[i].mid+1,r);
}
int main()
{
	int t,n,i,j;
	int a,b;
	int count=0;
	scanf("%d",&t);
	while(t--)
	{
		count++;
		char str[10];
		scanf("%d",&n);
		BuildTree(1,1,n);   //建立线段树
		printf("Case %d:\n",count);
		while(1)
		{
			scanf("%s",str);
			if(strcmp(str,"Add")==0)
			{
				scanf("%d%d",&a,&b);
				update(1,a,b);
			}
			else if(strcmp(str,"Sub")==0)
			{
				scanf("%d%d",&a,&b);
				update(1,a,-b);      //利用b与-b控制 美观值的增或者减
			}
			else if(strcmp(str,"Query")==0)
			{
				scanf("%d%d",&a,&b);
				printf("%d\n",Query(1,a,b));
			}
			else break;
		}
	}
	return 0;
}
    这只是最基础的线段树,后面的博客会更新各种变化的线段树等。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值