首先,让我们用一道简单的题来引入线段树:
输入n,m,在接下来的n行里输入一个数列a1,a2……an,
之后的m行输入三个数num,x,y其中num表示操作类型,
当num等于一时表示a[x]=y,等于二时表示输出x到y的最大值
很明显,用模拟就可以了,暴力上代码~~
#include<iostream>
using namespace std;
int n,m,a[1010];
void chuli(int x,int y)
{
a[x]=y;
return;
}
void work(int x,int y)
{
int ans=1<<31;
for(int i=x;i<=y;++i)
ans=max(ans,a[i]);
cout<<ans<<endl;
return;
}
int main()
{
freopen("test.in","r",stdin);
freopen("test1.out","w",stdout);
cin>>n>>m;
for(int i=1;i<=n;++i)
cin>>a[i];
for(int i=1;i<=m;++i){
int num,x,y;
cin>>num>>x>>y;
if(num==1)
chuli(x,y);
else
work(x,y);
}
return 0;
}
时间法度为o(n*m),如果n为一个较大的值的时候,就会超时。
接下来,介绍一个o(n*logn)的做法,我们可以将这个数组分段,并且求出每一段的最大值,如果是操作一,那么就将x所在的那一段重新求一个最大值。如果是操作二,那么求在x和y之间的分段的最大值的最大值,在求x和y所在的分段的最大值,他们的最大值就是x到y的最大值
附上代码:
#include<iostream>
#include<cmath>
using namespace std;
int n,m,a[100100],f[400];
int p;
void work(int x,int y)
{
int l=(x-1)/p+1;
int r=(y-1)/p+1;
int ans=1<<31;
if(l==r){
for(int i=x;i<=y;++i){
ans=max(ans,a[i]);
}
}
else{
for(int i=l+1;i<=r-1;++i)
ans=max(ans,f[i]);
for(int i=x;i<=l*p;++i)
ans=max(ans,a[i]);
for(int i=(r-1)*p+1;i<=y;++i)
ans=max(ans,a[i]);
}
cout<<ans<<endl;
return;
}
void operate(int x,int y)
{
a[x]=y;
int l=(x-1)/p+1;
f[l]=1<<31;
for(int i=(l-1)*p+1;i<=l*p;++i)
f[l]=max(f[l],a[i]);
}
int main()
{
freopen("test.in","r",stdin);
freopen("test2.out","w",stdout);
cin>>n>>m;
p=sqrt(n);
for(int i=1;i<=p+1;++i)
f[i]=(1<<31);
for(int i=1;i<=n;++i){
cin>>a[i];
f[(i-1)/p+1]=max(f[(i-1)/p+1],a[i]);
}
for(int i=1;i<=m;++i){
int num,x,y;
cin>>num>>x>>y;
if(num==1){
operate(x,y);
}
if(num==2)
work(x,y);
}
return 0;
}
但是,如果n的值是500000时还是会超时,所以,现在就要引出线段树了,刚开始是表示区间1到n之间的最大值,将这个区间从中间分开,分别求两边的最大值,直到区间中只有一个元素为止(即左右边界都是同一个数)
接下来就是操作了,如果是第一种操作,就将那个元素改值,然后再求最大值,然后往下面求最大值。有几种情况在程序中的check函数里已经十分清楚地表现了出来。
void change(int x,int l,int r,int p,int value)
{
if(l==r){
tree[x]=value;
return;
}
int m=(l+r)/2;
if(p<=m)
change(x+x,l,m,p,value);
else
change(x+x+1,m+1,r,p,value);
tree[x]=max(tree[x+x],tree[x+x+1]);
}
如果是第二种操作的话,就直接输出就好了(这个东西原本就是求一个区间的最大值的。。。)
最后附上代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<utility>
using namespace std;
const int maxN=100010;
int n,m,a[maxN],tree[maxN<<2];
void maketree(int x,int l,int r)
{
if(l==r){
tree[x]=a[l];
return;
}
int m=(l+r)/2;
maketree(x+x,l,m);
maketree(x+x+1,m+1,r);
tree[x]=max(tree[x+x],tree[x+x+1]);
}
void change(int x,int l,int r,int p,int value)
{
if(l==r){
tree[x]=value;
return;
}
int m=(l+r)/2;
if(p<=m)
change(x+x,l,m,p,value);
else
change(x+x+1,m+1,r,p,value);
tree[x]=max(tree[x+x],tree[x+x+1]);
}
int query(int x,int l,int r,int left,int right)
{
if(l==left&&r==right)
return tree[x];
int m=(l+r)/2;
if(right<=m)
return query(x+x,l,m,left,right);
else if(left>m){
return query(x+x+1,m+1,r,left,right);
}
else{
return max(query(x+x,l,m,left,m),query(x+x+1,m+1,r,m+1,right));
}
}
int main()
{
freopen("test.in","r",stdin);
freopen("test3.out","w",stdout);
cin>>n>>m;
for(int i=1;i<=n;++i)
cin>>a[i];
maketree(1,1,n);//O(n)
for(int i=1;i<=m;++i){
int num,x,y;
cin>>num>>x>>y;
if(num==1){
change(1,1,n,x,y);
}
else{
cout<<query(1,1,n,x,y)<<endl;
}
}
}