线段树:Balanced Lineup(pku 3264)(解题报告)

Balanced Lineup            
Time Limit:  5000MS     Memory Limit:  65536K  
Total Submissions:  11661  Accepted:  5459
Case Time Limit:  2000MS

Description

For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer John decides to organize a game of Ultimate Frisbee with some of the cows. To keep things simple, he will take a contiguous range of cows from the milking lineup to play the game. However, for all the cows to have fun they should not differ too much in height.

Farmer John has made a list of Q (1 ≤ Q ≤ 200,000) potential groups of cows and their heights (1 ≤ height ≤ 1,000,000). For each group, he wants your help to determine the difference in height between the shortest and the tallest cow in the group.

Input

Line 1: Two space-separated integers, N and Q
Lines 2..N+1: Line i+1 contains a single integer that is the height of cow i 
Lines N+2..N+Q+1: Two integers A and B (1 ≤ A ≤ B ≤ N), representing the range of cows from A to B inclusive.

Output

Lines 1..Q: Each line contains a single integer that is a response to a reply and indicates the difference in height between the tallest and shortest cow in the range.

Sample Input

6 3

1

7

3

4

2

5

1 5

4 6

2 2

Sample Output

6

3

0

Source

USACO 2007 January Silver


话说这是好多年前写的,哈哈哈哈,现在搬到csdn作为第一篇blog。

        这道题目的意思简要的来说,先给你cow的数量n以及问题的个数m,接下来n行分别代表第1 2...n头牛的height,再接下来m行的是问题询问区间[A,B]即第A头牛到第B头牛中最大的height和最小的height之差为多少。

       解题思路与步骤:

       1. 初始化:将1-n的区间序列转换成一个满足以下条件的数组(假定为a[n],可以对应一棵完全二叉树):

             mid=(a[i].left+a[i].right)/2;
            a[2*i].left=a[i].left;
            a[2*i].right=mid;
            a[2*i+1].left=mid+1;
            a[2*i+1].right=a[i].right;

      也就是把区间[1,n]看成一条线段,然后将其分两条子线段(分别做它的左子树和右子树),分界点为(左端点+右端点)/2,再将子线段分割,直到线段为(i,i)为止(叶子节点)。

     2. 插入:每输入一个节点x(本题里就是一个点,肯定是该线段树的叶子节点),就将其插入到该线段树中。

插入方法是,从根节点开始查找更新数据。对所有包含x的线段区间(x>=a[i].left&&x<=a[i].right),如果该区间的最大值小于插入的节点的值则更新,如果该区间的最小值大于插入的节点的值则更新,直到找到该节点为止。

     3. 询问:对于询问的区间[fr,to]的查找,我感觉本质和插入差不多,如果理解了插入,这里应该也不成问题了。同样,从根节点开始,如果是要查找的区间,查找结束;否则,如果有to<=mid,则查找该节点的左子树,如果有fr>mid,则查找其右子树,否则将该区间分成两部分查找。

剪枝:如果当前节点的区间最大值和最小值都不是最优的就不再对该节点进行向下查找。

     

     话说这就是我的第一道线段树的题目,所以呢,当然算是这类题目里比较简单的(也就是说非常适合初学者,O(∩_∩)O~),我也非常幸运的一次AC了。

      下面是程序代码还有非常详细的注释。


Source Code

Problem: 3264 User: sunriselin Memory: 2212K Time: 1688MS Language: C++ Result: Accepted

#include <stdio.h>
 
const int MAXNUM = 50005;
const int MIN = 0;
const int MAX = 10000000;
 
struct node{  
    int left,right; // 分别为该区间的左边界和右边界   
    int max,min;    // 分别为该区间的最大的height和最小的height
}tree[MAXNUM*3];
 
int maxnum,minnum;  // 分别记录要查找的区间中的最大值和最小值
 
void init(int l,int r,int i) // 初始化
{  
    tree[i].left=l;  
    tree[i].right=r; 
    tree[i].max=MIN;  
    tree[i].min=MAX;  
    if(l>=r) return ;  
    int mid=(l+r)/2;   
    init(l,mid,i*2);     // 初始化左子树   
    init(mid+1,r,i*2+1); // 初始化右子树   
    return ;
}
 
void insert(int i,int height,int pos) // 插入
{  
    if(i>=tree[pos].left&&i<=tree[pos].right) // i包含在该区间内   
    {    
        if(height<tree[pos].min) tree[pos].min=height;    
        if(height>tree[pos].max) tree[pos].max=height;  
    }  
    if(tree[pos].left==tree[pos].right) // 到达叶子节点,返回     
        return ;  
    int mid=(tree[pos].left+tree[pos].right)/2;  
    if(i<=mid) insert(i,height,pos*2); // 插入到左子树   
    else insert(i,height,pos*2+1);     // 插入到右子树   
    return ;
}
 
void query(int l,int r,int pos) // 询问
{  
    if(tree[pos].max<maxnum && tree[pos].min>minnum) // 小小的剪枝     
        return ;  
    if(tree[pos].left==l&&tree[pos].right==r) // 找到要查找的区间   
    {     
        if(tree[pos].max>maxnum) maxnum=tree[pos].max;     
        if(tree[pos].min<minnum) minnum=tree[pos].min;     
        return ;  
    }  
    else  
    {    
        int mid=(tree[pos].left+tree[pos].right)/2;    
        if(r<=mid) query(l,r,pos*2);       // 在左子树中查找     
        else if(l>mid) query(l,r,pos*2+1); // 在右子树中查找     
        else   // 没有合适的区间,将要查找的区间分成两部分     
        {       
            query(l,mid,pos*2);             
            query(mid+1,r,pos*2+1);    
        }  
    }  
    return ;
}
 
int main()
{  
    int num,ques,i,cow,fr,to;  
    while(scanf("%d%d",&num,&ques)!=EOF)  
    {    
        init(1,num,1);    
        for(i=1;i<=num;i++)    
        {      
            scanf("%d",&cow); // 输入每头牛的height      
            insert(i,cow,1);  // 在线段树中插入,从第1个节点开始匹配     
        }    
        while(ques--)    
        {      
            scanf("%d%d",&fr,&to);    // 输入询问区间       
            if(fr==to) printf("0\n"); // 查找的区间是长度为1,直接输出结果0      
            else      
            {        
                minnum=MAX;  // 初始          
                maxnum=MIN;        
                query(fr,to,1); // 询问,从节点1开始查找          
                printf("%d\n",maxnum-minnum);       
            }    
        } 
    } 
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值