线段树就是像下图的一种特殊的数,有存储,查询,修改等操作
需要用到
struct Node
{
int l,r;//存储边界值
int dt;//存储该区间需改变的值的和
int vis;//需要修改的值
Node *leftchild;
Node *rightchild;
};
线段树的操作主要分为两种:
1,对单个区间存储数值,查询,修改
2,对连续区间进行存储,查询,修改
起始两种操作就是一个函数的区别,难点也是在这个函数上。第二种操作多了一个函数的使用,难度会大点
线段数的使用需要用到三个函数,
1,初始化 2,修改 3查询
线段树的初始化就是以二分的思想建立一颗树,如需要建立区间(a,b)的线段树,先就是把(a,b) 二分为(a,c)
(d,b)。 (a,c)作为(a,b)的左孩子, (d,b)作为(a,b)的右孩子,以此类推
代码:
void make_build(Node *cur,int l,int r)//初始化
{
cur->l=l; cur->r=r; cur->dt=0; cur->vis=0;
if (r!=l)
{
cur->leftchild=new Node;
cur->rightchild=new Node;
make_build(cur->leftchild ,l ,(l+r)/2);
make_build(cur->rightchild ,(l+r)/2+1 ,r);
}
else
cur->leftchild=cur->rightchild=NULL;
}
接着就是修改,也是利用二分的思想,逐层去寻找符合要求的区间
要注意的就是一个函数
代码:
void make_update(Node *cur)
{
cur->leftchild->dt = cur->vis * (cur->leftchild->r-cur->leftchild->l+1);//向下传递
cur->rightchild->dt = cur->vis * (cur->rightchild->r-cur->rightchild->l+1);//向下传递
cur->leftchild->vis += cur->vis;//向下传递
cur->rightchild->vis += cur->vis;//向下传递
cur->vis=0;//标记,该层没有值向下传递
}
这个函数就像一个中转站,比如把(1,5)存入,我们的想法是当找到线段树中(1,5)区间就可以了,不要再继 续往下找,直到存到每一个叶子节点的区间去。但是我们要查找(1,2)时,我们会发现(1,2)区间并没有赋 值,这里就要用到void
make_update(Node *cur)函数。当查找(1,2)的值时,他的作用就是把(1,5)区间的值 向下传递。
查找函数其实就是把修改函数改下,没什么好讲的
完整代码
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
struct Node
{
int l,r;//存储边界值
int dt;//存储该区间需改变的值的和
int vis;//需要修改的值
Node *leftchild;
Node *rightchild;
};
void make_build(Node *cur,int l,int r)//初始化
{
cur->l=l; cur->r=r; cur->dt=0; cur->vis=0;
if (r!=l)
{
cur->leftchild=new Node;
cur->rightchild=new Node;
make_build(cur->leftchild ,l ,(l+r)/2);
make_build(cur->rightchild ,(l+r)/2+1 ,r);
}
else//忘记加
cur->leftchild=cur->rightchild=NULL;
}
void make_update(Node *cur)
{
cur->leftchild->dt = cur->vis * (cur->leftchild->r-cur->leftchild->l+1);
cur->rightchild->dt = cur->vis * (cur->rightchild->r-cur->rightchild->l+1);
cur->leftchild->vis += cur->vis;
cur->rightchild->vis += cur->vis;
cur->vis=0;
}
void make_change(Node *cur,int l,int r,int date)//修改
{
int md;
md=(cur->l+cur->r)/2;
if (cur->l==l && cur->r==r)
{
cur->dt += date * (r-l+1);
cur->vis += date;
}
else
{
if (cur->vis!=0)
{
make_update(cur);
}
if (r<=md)
{
make_change(cur->leftchild,l,r,date);//改变的区间在cur的左儿子上
}
if (l>md)
{
make_change(cur->rightchild,l,r,date);//改变的区间在cur的右儿子上
}
if (l<=md && r>md)//改变的区间横跨左右儿子区间
{
make_change(cur->leftchild,l,md,date);
make_change(cur->rightchild,md+1,r,date);
}
cur->dt=cur->leftchild->dt+cur->rightchild->dt;//忘记加
}
}
int make_query(Node *cur,int l,int r)//查找
{
int md,as;
as=0;
md=(cur->l+cur->r)/2;
if (cur->l==l && cur->r==r)
{
return cur->dt;
}
else
{
if (cur->vis!=0)
{
make_update(cur);
}
if (r<=md)
{
as+=make_query(cur->leftchild,l,r);
}
if (l>md)
{
as+=make_query(cur->rightchild,l,r);
}
if (l<=md && r>md)
{
as+=make_query(cur->leftchild,l,md);
as+=make_query(cur->rightchild,md+1,r);
}
return as;
}
//return as;放在外面
}
int main()
{
int n,m,p;
int i,j,k;
int a,b,c;
Node A;
A.l=0; A.r=0; A.dt=0;
A.leftchild=NULL;
A.rightchild=NULL;
while (~scanf ("%d",&n))
{
make_build(&A,1,n);
p=1;
while (p)
{
printf(".............................\n");
printf("修改区间值,请输入 1 \n");
printf("查寻区间值,请输入 2 \n");
printf("结束请输入 0 \n");
scanf ("%d",&k);
if (k==1)
{
printf("输入区间a,b 和改变的值 c\n");
scanf ("%d%d%d",&a,&b,&c);
make_change(&A,a,b,c);
}
if (k==2)
{
m=0;
printf("请输入要查询的区间a,b \n");
scanf ("%d%d",&a,&b);
m=make_query(&A,a,b);
printf("%d\n",m);
}
if (k==0)
p=0;
}
break;
}
return 0;
}