对我个人而言,什么是线段树?就是将一个长长的大区间分为一段段小区间,然后,每段小区间按照某种方式分配了大区间里的值或者元素。
上题之前讲一下怎么制造这个树?怎么初步的用这个树?这个树的功能。(因为初学,讲的会有点通熟易懂)
就像学软机的链表,二叉树一样,我们构造一个线段树也将用结构体类型去定义它:这个线段树需要有它在当前位置上的值,需要有它的区间段(左右端点),同时如果它有子树的话,还需要用指针型分别指向它的左子树和右子树。
附上代码:
struct Tree
{
int date;
int left;
int right;
Tree *lchild;
Tree *rchild;
};
接下来,我们既然有了树的模版就要创建树,怎样creat一个树,我们就需要理解线段树了,很多像我一样的萌新认识线段树应该都会从网上学到:如果根子树的区间为[a, b],那么它的左子树区间为[a,(a+b)/2],右子树区间为[(a+b)/2+1,b]。所以有的人用左子树[a,(a+b)>>1],右子树[(a+b)>>1+1,b]这样的骚操作表示(“>>”正数左移一位相当于对2整除)。当到叶子节点(即a==b,无子树时)的那个子树时,给予它附上值(也有时并非如此,此题需要而已)。
ps:提示,一定要用malloc()开内存,不然就会死掉,学习链表,二叉树等所带来的经验了。
我一开始malloc()没用对,导致我一直莫名其妙Wrong,因为sizeof()里一定要放类名。
附上代码:
Tree *creatTree(int a,int b)
{
Tree *r;
r=(Tree*)malloc(sizeof(Tree));
r->left=a;
r->right=b;
if(a==b)
{
r->date=num[a];
r->lchild=NULL;
r->rchild=NULL;
}
else
{
int mid=(a+b)/2;
r->lchild=creatTree(a, mid);
r->rchild=creatTree(mid+1, b);
r->date = r->lchild->date + r->rchild->date;
}
return r;
}
接下来,我们要对这个已经初步成形的线段树进行操作,那么就得需要写些子函数了,为了使得形参的改变等同于实参的改变,我们必须用上指针,然后再逐步查找所对应的点,并对它进行操作。
void Tree_insert(Tree *r,int a,int b)
{
if(r->left==a&&r->right==a)
{
r->date+=b;
return;
}
int mid=(r->left+r->right)/2;
if(a<=mid)
{
Tree_insert(r->lchild, a, b);
}
else
{
Tree_insert(r->rchild, a, b);
}
r->date+=b;
}
接下来,我们需要一个查找函数,找到对应某个区间(a, b)的值。那么我们就会想,如果这个区间(a, b)不在那些个已经存在的区间怎么办?举个例子:大区间(1,5)它的子区间只有(1, 3), (4, 5), (1, 2), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)。假如我们需要找个(2,4)区间内的值岂不是没有办法?不会的,寻找区间(2,4)范围虽然不在这些个子区间段内,但它恰好可以用(2, 2), (3, 3), (4, 4)的和来表示,那么这样的问题就解决了。
void Tree_find(Tree *r,int a,int b)
{
if(r->left==a&&r->right==b)
{
sum+=r->date;
return;
}
int mid=(r->left+r->right)/2;
if(a>mid)
{
Tree_find(r->rchild, a, b);
}
else if(b<=mid)
{
Tree_find(r->lchild, a, b);
}
else
{
Tree_find(r->lchild, a, mid);
Tree_find(r->rchild, mid+1, b);
}
}
那么,这道题所需要的功能便齐全了,附上AC代码:
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
using namespace std;
int num[50005];
int n;
int sum;
string work;
int st,en;
struct Tree
{
int date;
int left;
int right;
Tree *lchild;
Tree *rchild;
};
Tree *creatTree(int a,int b)
{
Tree *r;
r=(Tree*)malloc(sizeof(Tree));
r->left=a;
r->right=b;
if(a==b)
{
r->date=num[a];
r->lchild=NULL;
r->rchild=NULL;
}
else
{
int mid=(a+b)/2;
r->lchild=creatTree(a, mid);
r->rchild=creatTree(mid+1, b);
r->date = r->lchild->date + r->rchild->date;
}
return r;
}
void Tree_insert(Tree *r,int a,int b)
{
if(r->left==a&&r->right==a)
{
r->date+=b;
return;
}
int mid=(r->left+r->right)/2;
if(a<=mid)
{
Tree_insert(r->lchild, a, b);
}
else
{
Tree_insert(r->rchild, a, b);
}
r->date+=b;
}
void Tree_find(Tree *r,int a,int b)
{
if(r->left==a&&r->right==b)
{
sum+=r->date;
return;
}
int mid=(r->left+r->right)/2;
if(a>mid)
{
Tree_find(r->rchild, a, b);
}
else if(b<=mid)
{
Tree_find(r->lchild, a, b);
}
else
{
Tree_find(r->lchild, a, mid);
Tree_find(r->rchild, mid+1, b);
}
}
int main()
{
Tree *t;
int T;
scanf("%d%*c",&T);
int ca=0;
while(T--)
{
ca++;
memset(num, 0, sizeof(num));
printf("Case %d:\n",ca);
scanf("%d%*c",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
}
t=creatTree(1, n);
while(cin>>work&&work!="End")
{
if(work!="End")
{
cin>>st>>en;
if(work=="Add")
{
Tree_insert(t, st, en);
}
else if(work=="Sub")
{
Tree_insert(t, st, -en);
}
else if(work=="Query")
{
sum=0;
Tree_find(t, st, en);
printf("%d\n",sum);
}
}
else break;
}
}
return 0;
}
谢谢观看,欢迎评论留言。