线段树——基础题

题目

1、POJ 3264 Balanced Lineup 求区间的最大值-最小值

题意:一个农夫有N头牛(1≤N≤50000),现在农夫想查询一定范围内的奶牛的高度最大值和最小值的差,奶牛查询操作数量为(1 ≤N ≤ 50,000),查询次数为(1 ≤Q ≤ 200,000)每头奶牛的身高(1<=身高<=1000,000)。

分析:该题可以采用线段树来做,利用线段树,根据线段树的性质,我们可以将其最大值和最小值保留下来,根据线段树的性质,所有的叶子节点即为相应奶牛高度,其父亲节点保存其子节点的最大值和最小值,依次重复,时间复杂度为(O(log(n))。

代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>

using namespace std;

#define Mid ((l+r)>>1)  				//二分区间 
#define lson i<<1,l,Mid					//左子数 
#define rson i<<1|1,Mid+1,r				//右子数 
 
const int MAXN= 50005;					//奶牛数量 
const int INF=0x3f3f3f3f;				//无限大常量 
int Max[MAXN<<2],Min[MAXN<<2];			//最大最小值数组,大小为数据的四倍 
int ans1,ans2;				
/**创建线段树*/ 
void build(int i,int l,int r)
{
    if(l==r){							//如果区间长度为0,该节点为一个点 
        scanf("%d",&Max[i]);			//输入数据 
        Min[i] = Max[i];
    }else{
        build(lson);					//否则继续建左子数 
        build(rson);					//否则继续建右字数 
        Max[i] = max( Max[i<<1], Max[i<<1|1]);			//取左右孩子中权值大的 
        Min[i] = min( Min[i<<1], Min[i<<1|1]);			//取左右孩子中权值小的 
    }
}

/**区间查询*/ 
void query(int i,int l,int r,int L,int R)
{
    if(L <= l && r <= R){				//如果该节点区间完全属于被查询的结点 
        ans1 = max(ans1,Max[i]);		//从当前ans1与Max[i]中选出大值 
        ans2 = min(ans2,Min[i]);		//从当前ans2与MIn[i]中选出小值 
    }else{
        if( L <= Mid)					//查询的左值小与中点建左子数 
            query(lson,L,R);
        if( R > Mid)					//查询的右值大于中点建右子数 
            query(rson,L,R);		
    }
}
 
int main()
{
    int n,m,L,R;
    while(scanf("%d%d",&n,&m)!=EOF){
        build(1,1,n);					//从根节点开始建树 
        while(m--){
            ans1 = -INF,ans2 = INF;		//ans1赋值为无限小,ans2赋值为无限大 
            scanf("%d%d",&L,&R);
            query(1,1,n,L,R);			//查询 
            printf("%d\n", ans1-ans2);	//输出高度差 
        }
    }
    return 0;
}

2、HDU 1754 I Hate It 单点更新,区间查询最大

题意:一个班N个人,老师进行点名操作共M次,可以查询区间范围内成绩最高的人,或者更新该学生的分数。 

M ( 0<N<=200000,0<M<5000 )

分析:直接套线段树模板。进行更新,查找操作。

代码:

#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;

#define Mid ((l+r)>>1)     
#define lson i<<1,l,Mid
#define rson i<<1|1,Mid+1,r
 
const int MAXN = 200000+5;			//学生数目 
const int INF = 0x3f3f3f3f;			//无限大 
int Max[MAXN<<2];					//储存最大值,大小为数据的四倍 
int ans1,ans2;
/**创建线段树*/ 
void build(int i,int l,int r)
{
    if(l==r){						//为一个点时 
        scanf("%d",&Max[i]);		//输入数据 
    }else{
        build(lson);				//创建左子数 
        build(rson);				//创建右子数 
        Max[i] = max( Max[i<<1], Max[i<<1|1]);		//该节点的权值为左右子数中较大的那个 
    }
}

/**更新结点*/ 
void update(int i,int l,int r,int pos,int num)
{
    if(l == r && r == pos){			//如果为一个点,并且是要更新的结点时 
        Max[i] = num;				//更新结点 
    }else{
        if( pos <= Mid)				//如果pos小于中点 
            update(lson,pos,num);	//在左子数上更新 
        if( pos > Mid)				//如果pos大于中点 
            update(rson,pos,num);	//在右子数上更新 
        Max[i] = max( Max[i<<1], Max[i<<1|1]);		//该结点的权值为孩子中较大的那个
		 
    }
}

/**查询*/ 
int query(int i,int l,int r,int L,int R)
{
    if(L <= l && r <= R){
        return Max[i];			//完全重合时	
    }else{
        int tmp = -1;
        if( L <= Mid)			//左子树 
            tmp = max(tmp,query(lson,L,R));
        if( R > Mid)			//右子树 
            tmp = max(tmp,query(rson,L,R));
        return tmp;
    }
}
 
int main()
{
    int n,m,L,R;
    char op;
    //循环输入,当无输入时结束循环 
    while(scanf("%d%d",&n,&m)!=EOF){	
        build(1,1,n);				//从根节点开始建树 
        getchar();	
        while(m--){					//循环进行操作控制 
            scanf("%c%d%d%*c",&op,&L,&R);		//输入要进行的操作 
            /*判断操作的类型*/
			if(op=='Q')
                printf("%d\n",query(1,1,n,L,R));
            else
                update(1,1,n,L,R);
        }
    }
    return 0;
}

3、HDU 1166 敌兵布阵 单点更新,查询区间和

题意:有N个工兵营,告诉你每个工兵营M人。然后进行各种操作接下来每行有一条命令,命令有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条命令

分析:线段树的基础题,建立线段树之后,判断操作的类型,进行单点的更改或者查询区间和。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>

using namespace std;
 
#define Mid ((l+r)>>1)
#define lson i<<1,l,Mid
#define rson i<<1|1,Mid+1,r
 
const int MAXN = 50000+5;		//工兵营数量 
const int INF = 0x3f3f3f3f;		//无限大常量 
int sum[MAXN<<2];				//求和数组,大小为数据的四倍 
int ans1,ans2;
/**建立线段树*/ 
void build(int i,int l,int r)
{
    if(l==r){	
        scanf("%d",&sum[i]);	//输入数据 
    }else{
        build(lson);			//建左子树 
        build(rson);			//建右子树 
        sum[i] = sum[i<<1] + sum[i<<1|1];		//结点权值等于孩子权值相加 
    }
}
/**更新数据*/
void update(int i,int l,int r,int pos,int num)
{
    if(l == r && r == pos){     
        sum[i] += num;							//记录更改的数值大小 
    }else{
        if( pos <= Mid)
            update(lson,pos,num);				//更新左孩子
        else        
            update(rson,pos,num);				//更新右孩子 
        sum[i] = sum[i<<1] + sum[i<<1|1];		//结点权值等于孩子权值相加 
    }
}
/**查找*/ 
int query(int i,int l,int r,int L,int R)
{
    if(L <= l && r <= R){
        return sum[i];							//符合条件时 
    }else{
        int tmp = 0;
        if( L <= Mid)							 
            tmp += query(lson,L,R);				//找左孩子 
        if( R > Mid)
            tmp += query(rson,L,R);				//找右孩子 
        return tmp;
    }
}
 
int main()
{
    int n,m,L,R,t;
    char op[10];
    scanf("%d",&t);								
    for(int cas=1;cas<=t;cas++){				//t组数据 
        scanf("%d",&n);							//n个数据 
        build(1,1,n);							//从根结点建树 
        printf("Case %d:\n",cas);
        while(scanf("%s",op),op[0]!='E'){		//End结束循环 
            scanf("%d%d",&L,&R);
            if(op[0]=='Q')						//Query求区间和 
                printf("%d\n", query(1,1,n,L,R));
            else if(op[0]=='A')					//增加 
                update(1,1,n,L,R);
            else
                update(1,1,n,L,-R);				//减少 
        }
    }
    return 0;
}
发布了22 篇原创文章 · 获赞 14 · 访问量 1194

猜你喜欢

转载自blog.csdn.net/weixin_44157233/article/details/98028741
今日推荐