线段树&N - I Hate It

試著忘記壹切 提交于 2019-12-18 05:28:31

转载处:http://blog.csdn.net/metalseed/article/details/8039326 

一:线段树基本概念

1:概述

线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)!

性质:父亲的区间是[a,b],(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b],线段树需要的空间为数组大小的四倍

2:基本操作(查询区间最小值)

线段树的主要操作有:

(1):线段树的构造 void build(int node, int begin, int end);

主要思想是递归构造,如果当前节点记录的区间只有一个值,则直接赋值,否则递归构造左右子树,最后回溯的时候给当前节点赋值

void build(int node, int begin, int end)    
{    
    if (begin == end)    
        segTree[node] = array[begin]; /* 只有一个元素,节点记录该单元素 */  
    else    
    {     
        /* 递归构造左右子树 */   
        build(2*node, begin, (begin+end)/2);    
        build(2*node+1, (begin+end)/2+1, end);   
           
        /* 回溯时得到当前node节点的线段信息 */    
        if (segTree[2 * node] <= segTree[2 * node + 1])    
            segTree[node] = segTree[2 * node];    
        else    
            segTree[node] = segTree[2 * node + 1];    
    }    
}
View Code

(2):区间查询int query(int node, int begin, int end, int left, int right);

(其中node为当前查询节点,begin,end为当前节点存储的区间,left,right为此次query所要查询的区间)

主要思想是把所要查询的区间[a,b]划分为线段树上的节点,然后将这些节点代表的区间合并起来得到所需信息

int query(int node, int begin, int end, int left, int right)    
{   
    int p1, p2;    
    
    /*  查询区间和要求的区间没有交集  */  
    if (left > end || right < begin)    
        return -1;    
    
    if (begin >= left && end <= right)    
        return segTree[node];    
      
    p1 = query(2 * node, begin, (begin + end) / 2, left, right);   
    p2 = query(2 * node + 1, (begin + end) / 2 + 1, end, left, right);    
    
       
    if (p1 == -1)    
        return p2;    
    if (p2 == -1)    
        return p1;    
    if (p1 <= p2)    
        return  p1;    
    return  p2;      
}  
View Code

(3):区间或节点的更新及线段树的动态维护update (这是线段树核心价值所在,节点中的标记域可以解决N多种问题)

动态维护需要用到标记域,延迟标记等。

a:单节点更新

void Updata(int node, int begin, int end, int ind, int add)/*单节点更新*/    
{    
    
    if( begin == end )    
    {    
        segTree[node] += add;    
        return ;    
    }    
    int m = ( left + right ) >> 1;    
    if(ind <= m)    
        Updata(node * 2,left, m, ind, add);    
    else    
        Updata(node * 2 + 1, m + 1, right, ind, add);    
    /*回溯更新父节点*/    
    segTree[node] = min(segTree[node * 2], segTree[node * 2 + 1]);     
         
}   
View Code

N - I Hate It

很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。 
这让很多学生很反感。 

不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。

Input本题目包含多组测试,请处理到文件结束。 
在每个测试的第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目。 
学生ID编号分别从1编到N。 
第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。 
接下来有M行。每一行有一个字符 C (只取'Q'或'U') ,和两个正整数A,B。 
当C为'Q'的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。 
当C为'U'的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。 
Output对于每一次询问操作,在一行里面输出最高成绩。Sample Input

5 6
1 2 3 4 5
Q 1 5
U 3 6
Q 3 4
Q 4 5
U 2 9
Q 1 5

Sample Output

5
6
5
9

 题解:直接套线段树的三个模板即可;

代码:

#include<iostream>
#include<cstdio>
using namespace std;
const int mm=200005;
int segTree[mm*4 + 10];
int a[mm + 10];
void build(int node,int istart,int iend)
{
    if(istart==iend)
        segTree[node]=a[istart];
    else
    {
        int mid=(istart+iend)/2;
        build(node*2,istart,mid);
        build(node*2+1,mid+1,iend);
        if(segTree[node*2]>=segTree[node*2+1])
            segTree[node]=segTree[node*2];
        else
            segTree[node]=segTree[node*2+1];
    }
}

int query(int node,int istart,int iend,int left,int right)
{
    int p1,p2;
    if(left>iend || right<istart)
        return -1;
    if(istart>=left&&iend<=right)
        return segTree[node];
    p1=query(node*2,istart,(istart+iend)/2,left,right);
    p2=query(node*2+1,(istart+iend)/2+1,iend,left,right);
    return max(p1,p2);
}

void update(int node,int istart,int iend,int ind,int add)
{
    if(istart==iend)
    {
        segTree[node]=add;
        return ;
    }
    int m=(istart+iend)>>1;
    if(ind<=m)
        update(node*2,istart,m,ind,add);
    else
        update(node*2+1,m+1,iend,ind,add);
    segTree[node]=max(segTree[node*2],segTree[node*2+1]);
}
int main()
{
    int n,k,i,x,b,ans;
    char s;
    while(scanf("%d %d",&n,&k)!=EOF){
        for(i=1;i<=n;i++)
            scanf("%d",&a[i]);
            build(1,1,n);
        while(k--){
            cin>>s;
            scanf("%d %d",&x,&b);
            if(s=='Q')
            {
                ans=query(1,1,n,x,b);
                cout<<ans<<endl;
            }
            else
            {
                update(1,1,n,x,b);
            }
        }
        }
        return 0;
}
View Code

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!