在代码前先介绍一些我的线段树风格:
- 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
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;
}
这只是最基础的线段树,后面的博客会更新各种变化的线段树等。