poj 3264 线段树/RMQ(区间最大减最小)

题意:给定Q(1 ≤ Q≤ 200,000)个数A1,A2… AQ,,多次求任一区间Ai–Aj中最大数和最小数的差。

思路:1、线段树。节点要存放区间内的最大值和最小值。

线段树建立也可以不使用指针,而用数组代替:若根节点下标为0,假设线段树上某节点下标为i,则其左子节点下标为i *2 + 1, 右子节点下标为i * 2+ 2。若根节点为1,则左子树为i*2,右子树为i*2+1。

2、RMQ,就是裸的最大最小RMQ不需多解释。

指针建树代码:

#include <stdio.h>
#include <string.h>
#define N 200005
#define MIN 0x7fffffff
#define MAX -MIN
typedef struct node{
	int left,right;//区间起点和终点
	int min,max;//本区间里的最大最小值
	struct node *lson,*rson;
}Tree;
int n,q,resmin,resmax,mid;
Tree t[N*2+1];
Tree *root,*alloc;
int min(int a,int b){
	if(a<b)
		return a;
	return b;
}
int max(int a,int b){
	if(a>b)
		return a;
	return b;
}
int getmid(Tree *x){
	return (x->left+x->right)/2;
}
void create(Tree *r,int L,int R){
	r->left= L;
	r->right = R;
	r->max = MAX;
	r->min = MIN;
	if(L!=R){
		mid=getmid(r);
		r->lson = ++alloc;
		create(r->lson,L,mid);
		r->rson = ++alloc;
		create(r->rson,mid+1,R);
	}
}
void insert(Tree *r,int index,int x){//将第i个数,其值为v,插入线段树
	mid=getmid(r);
	r->min = min(r->min,x);
	r->max = max(r->max,x);
	if(r->left==index && r->right==index)
		return ;
	if(index <= mid)
		insert(r->lson,index,x);
	else
		insert(r->rson,index,x);
}
void query(Tree *r,int L,int R){//查询区间[L,R]中的最小值和最大值,如果更优就记在全局变量里
	mid = getmid(r);
	if(resmax>r->max && resmin<r->min)//剪枝
		return ;
	if(L == r->left && R == r->right){
		resmin = min(resmin,r->min);
		resmax = max(resmax,r->max);
	}else if(R <= mid)
		query(r->lson,L,R);
	else if(L > mid)
		query(r->rson,L,R);
	else{
		query(r->lson,L,mid);
		query(r->rson,mid+1,R);
	}
}
int main(){
	freopen("a.txt","r",stdin);
	while(scanf("%d %d",&n,&q)!=EOF){
		int i,a,b,num;
		root = alloc = t;
		create(root,1,n);
		for(i = 1;i<=n;i++){
			scanf("%d",&num);
			insert(root,i,num);
		}
		for(i = 1;i<=q;i++){
			scanf("%d %d",&a,&b);
			resmin = MIN;
			resmax = MAX;
			query(root,a,b);
			printf("%d\n",resmax-resmin);
		}
	}
	return 0;
}

数组建树代码:

#include <stdio.h>
#include <string.h>
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define N 200005
struct node{
    int left,right,up,down;
}t[N*2+1];
int n,q,big,sma;
int midnum(int a,int b){
    return (a+b)>>1;
}
void create(int id,int a,int b){
    int mid = midnum(a,b);
    t[id].left = a;
    t[id].right = b;
    t[id].up = 0;
    t[id].down = 0x3fffffff;
    if(a == b)
        return;
    create(id*2, a, mid);
    create(id*2+1, mid+1, b);
}
void insert(int root,int id,int x){//将序号为id位置插入数字x
    t[root].up = max(t[root].up , x);
    t[root].down = min(t[root].down , x);
    if(t[root].right==id && t[root].left==id)
        return;
    if(id<=(t[root].left+t[root].right)/2)
        insert(root*2, id, x);
    else
        insert(root*2+1, id, x);
}
void query(int root,int a,int b){
    int mid = midnum(t[root].left,t[root].right);
    if(t[root].left == a && t[root].right==b){
        big = max(big,t[root].up);
        sma = min(sma,t[root].down);
        return ;
    }
    if(b<=mid)
        query(root*2, a, b);
    else if(a>mid)
        query(root*2+1, a, b);
    else{
        query(root*2, a, mid);
        query(root*2+1, mid+1, b);
    }
}
int main(){
    int i,x,y;
    scanf("%d %d",&n,&q);
    create(1,1,n);
    for(i = 1;i<=n;i++){
        scanf("%d",&x);
        insert(1,i,x);
    }
    for(i = 1;i<=q;i++){
        big = 0;
        sma = 0x3fffffff;
        scanf("%d %d",&x,&y);
        query(1,x,y);
        printf("%d\n",big-sma);
    }
}

RMQ:

#include <cstdio>
#include <cstring>
#include <cmath>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define N 50005
#define Q 200005
using namespace std;
int dmax[N][20],dmin[N][20],s[N];
int n,q;
void st(int n){
    int i,j,k = log((double)(n+1))/log(2.);
    for(j = 1;j<=n;j++)
        dmax[j][0] =  dmin[j][0] = s[j];
    for(j = 1;j<=k;j++)
        for(i = 1;i+(1<<j)-1<=n;i++){
            dmax[i][j] = max(dmax[i][j-1], dmax[i+(1<<(j-1))][j-1]);
            dmin[i][j] = min(dmin[i][j-1], dmin[i+(1<<(j-1))][j-1]);
        }
}
int querymax(int a,int b){
    int k = log((double)(b-a+1))/log(2.);
    return max(dmax[a][k], dmax[b-(1<<k)+1][k]);
}
int querymin(int a,int b){
    int k = log((double)(b-a+1))/log(2.);
    return min(dmin[a][k], dmin[b-(1<<k)+1][k]);
}
int main(){
    int i,a,b,x,y;
    scanf("%d %d",&n,&q);
    for(i = 1;i<=n;i++)
        scanf("%d",&s[i]);
    st(n);
    for(i = 0;i<q;i++){
        scanf("%d %d",&a,&b);
        x = querymin(a,b);
        y = querymax(a,b);
        printf("%d\n",y-x);
    }
    return 0;
}


你可能感兴趣的:(poj 3264 线段树/RMQ(区间最大减最小))