线段树的简单操做
一些线段数的基本操作
比如建树,单点修改,单点查询, 单点修改和区间查询(区间和), 区间修改和区间查询,甚至 单点询问,区间修改。这些操作都是有着差异性。尤其是区间修改,需要用到Llazy标记。
另外,建树也分为很多种(取决于每个父结点的意义)
比如以最大或最小值建树(有的题还让你求一个区间内最大与最小值之间的落差,这种情况只要开一个结构体即可),还有区间和。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 400010
int inf=99999999;
using namespace std;
long long mi[N],a[N];
long long p=1,q=1;
void buildmin(int k,int l,int r) //最小值建树
{
if(l==r)
{
mi[k]=a[l];
return;
}
int mid=(l+r)/2;
buildmin(k<<1,l,mid);
buildmin(k<<1|1,mid+1,r);
mi[k]=min(mi[k<<1],mi[k<<1|1]);//自下而上,当前K取左右儿子较小,**该位置的条件因题而异**
}
void buildsum(int k,int l,int r) //求和建树
{
if(l==r)
{
mi[k]=a[l];
return;
}
int mid=(l+r)/2;
buildsum(k<<1,l,mid);
buildsum(k<<1|1,mid+1,r);
mi[k]=mi[k<<1]+mi[k<<1|1]; //变化仅此而已
}
int query_min(int k,int l,int r,int x,int y) //简单的区间询问操作
{
if(r<x||l>y)return inf;
if(x<=l&&r<=y)return mi[k];
int mid=(l+r)/2;
return min(query_min(k<<1,l,mid,x,y),query_min(k<<1|1,mid+1,r,x,y)); //果然,最小值建树区间访问求最小值比较容易....
}
void change(int k,int l,int r,int x,int v) //点修改操作
{
if(l>x||r<x)return;
if(l==r&&l==x)
{
mi[k]=v;
return;
}
int mid=(l+r)/2;
change(k<<1,l,mid,x,v);
change(k<<1|1,mid+1,r,x,v);
mi[k]=min(mi[k<<1],mi[k<<1|1]); //感觉和建树bulid类似
}
int main()
{
int i,j,n;
scanf("%d",&n);
for(i=1; i<=n; i++)
scanf("%lld",&a[i]);
//1.最小值建树检验
buildmin(1,1,n);
for(i=1; i<=n*2-1; i++)
printf("%lld ",mi[i]);
printf("\n");
//2.把第X个数变成V,检验
change(1,1,n,1,2);
for(i=1; i<=n*2-1; i++)
printf("%lld ",mi[i]);
printf("\n");
//3.最小值区间访问,访问区间:[t1,t2]
int t1,t2,tt;
scanf("%d%d",&t1,&t2);
tt=query_min(1,1,n,t1,t2);
printf("%d\n",tt);
//4.和建树检验
buildsum(1,1,n);
for(i=1; i<=n*2-1; i++)
printf("%lld ",mi[i]);
printf("\n");
}
/*
测试数据:
8
5 3 7 9 6 4 1 2
3 4
3 4 表示查询区间[3,4]的最小值,应该为7
最小值建数注意:
1.最好build(1,1,n)
2.l=r时,mi[k]=a[l],这个时候的l正号对应相应序列的ID
3.建树mi[4*n]
4.(n<<1)-1 括号不能少 (n<<1-1..算术优先)
*/