nyoj 1185 最大最小值 【线段树】

最大最小值

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 2
描述
给出N个整数,执行M次询问。
对于每次询问,首先输入三个整数C、L、R:

    如果C等于1,输出第L个数到第R个数之间的最小值;

    如果C等于2,输出第L个数到第R个数之间的最大值;

    如果C等于3,输出第L个数到第R个数之间的最小值与最大值的和。

(包括第L个数和第R个数)。

输入
首先输入一个整数T(T≤100),表示有T组数据。
对于每组数据,先输入一个整数N(1≤N≤10000),表示有N个整数;
接下来一行有N个整数a(1≤a≤10000);
然后输入一个整数M,表示有M次询问;
接下来有M行(1≤M≤10000),每行有3个整数C、L、R(1≤C≤3,1≤L≤R≤N)。
输出
按照题意描述输出。每个输出占一行。
样例输入
2
4
1 3 2 4
2
1 1 4
2 2 3
5
1 2 3 4 5
1
3 1 5
样例输出
1
3
6
来源
原创
上传者

TC_李远航


思路:

        求最大值就需要建立最大树,求最小值就需要建立最小数,然后建立之后就只需要进行查找了,建立的时候可以同时建立两个树,但是当你查找的时候因为你的需要不同,所以你就需要写两个实现不同功能的函数,一个是实现求最大值的函数,也就是在最大树中找最大值,在最小树中找最小值,就OK了!


代码如下:


方法一:


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
int n,m;
int tree1[40005];
int tree2[40005];
int c,l,r;
int min(int a,int b)
{
	if(a>b)
		return b;
	return a;
}
int max(int a,int b)
{
	if(a>b)
		return a;
	return b;
}
void build(int rt,int l,int r)
{
	if(l==r)
	{
		scanf("%d",&tree1[rt]);
		tree2[rt]=tree1[rt];
		return;
	}
	int mid=(l+r)>>1,pt=rt<<1;
	build(pt,l,mid);
	build(pt|1,mid+1,r);
	tree1[rt]=min(tree1[pt],tree1[pt|1]);
	tree2[rt]=max(tree2[pt],tree2[pt|1]);	
}
int find1(int rt,int l,int r,int a,int b)
{
	if(l>=a&&r<=b)
	{
		return tree1[rt];
	}
	int mid=(l+r)>>1,pt=rt<<1,res=INF;
	if(a<=mid)
	{
		res=min(find1(pt,l,mid,a,b),res);
	}
	if(b>mid)
	{
		res=min(find1(pt|1,mid+1,r,a,b),res);
	}
	return res;
}
int find2(int rt,int l,int r,int a,int b)
{
	if(l>=a&&r<=b)
	{
		return tree2[rt];
	}
	int mid=(l+r)>>1,pt=rt<<1,res=-1;
	if(a<=mid)
	{
		res=max(find2(pt,l,mid,a,b),res);//在它的左子树中找最小值 
	}//对应的数组的下标也是他的下标的2倍,pt代表的就是l到mid中的最小值
	//如果满足l到mid在a,b的范围内说明找到最小值了,直接返回,将所有的a,b范围内的
	//最小值都找到之后进行比较,输出最小的值! 
	if(b>mid)//这个是在右子树中找最小值
	{//pt|1代表的是mid+1到r中的最小值,如果不在a,b区间里面,就继续递归,如果在a,b区间里面就直接返回最大值 
		res=max(find2(pt|1,mid+1,r,a,b),res);
	} 
	return res;
}
int main()
{
	 int T;
	 scanf("%d",&T);
	 while(T--)
	 {
	 	scanf("%d",&n);
	 	build(1,1,n);
	 	scanf("%d",&m);
	 	while(m--)
	 	{
	 		scanf("%d%d%d",&c,&l,&r);
	 		if(c==1)
	 		{
	 			printf("%d\n",find1(1,1,n,l,r));
	 		}
	 		else if(c==2)
	 		{
	 			printf("%d\n",find2(1,1,n,l,r));
	 		}
	 		else if(c==3)
	 		{
	 			printf("%d\n",find1(1,1,n,l,r)+find2(1,1,n,l,r));
	 		}
	 	}
	 }
	 return 0;
}

方法二:


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
int n,m;
int tree1[40005];
int tree2[40005];
int c,l,r;
int min(int a,int b)
{
	if(a>b)
		return b;
	return a;
}
int max(int a,int b)
{
	if(a>b)
		return a;
	return b;
}
void build(int rt,int l,int r)
{
	if(l==r)
	{
		scanf("%d",&tree1[rt]);
		tree2[rt]=tree1[rt];
		return;
	}
	int mid=(l+r)>>1,pt=rt<<1;
	build(pt,l,mid);
	build(pt|1,mid+1,r);
	tree1[rt]=min(tree1[pt],tree1[pt|1]);
	tree2[rt]=max(tree2[pt],tree2[pt|1]);	
}
int find1(int rt,int l,int r,int a,int b)
{//这个方法是要么在左子树里面查找,要么在右子树中查找,要么在左右子树中查找
//其实是一样的! 
	if(l>=a&&r<=b)
	{
		return tree1[rt];
	}
	int mid=(l+r)>>1,pt=rt<<1,res=INF;
	if(b<=mid)
	{
		res=min(find1(pt,l,mid,a,b),res);
	}
	else if(a>mid)
	{
		res=min(find1(pt|1,mid+1,r,a,b),res);
	}
	else
	{
		res=min(find1(pt,l,mid,a,b),res);
		res=min(find1(pt|1,mid+1,r,a,b),res);
	}
	return res;
}
int find2(int rt,int l,int r,int a,int b)
{
	if(l>=a&&r<=b)
	{
		return tree2[rt];
	}
	int mid=(l+r)>>1,pt=rt<<1,res=-1;
	if(b<=mid)
	{
		res=max(find2(pt,l,mid,a,b),res);
	}
	else if(a>mid)
	{
		res=max(find2(pt|1,mid+1,r,a,b),res);
	}
	else
	{
		res=max(find2(pt,l,mid,a,b),res);
		res=max(find2(pt|1,mid+1,r,a,b),res);
	}
	return res;
}
int main()
{
	 int T;
	 scanf("%d",&T);
	 while(T--)
	 {
	 	scanf("%d",&n);
	 	build(1,1,n);
	 	scanf("%d",&m);
	 	while(m--)
	 	{
	 		scanf("%d%d%d",&c,&l,&r);
	 		if(c==1)
	 		{
	 			printf("%d\n",find1(1,1,n,l,r));
	 		}
	 		else if(c==2)
	 		{
	 			printf("%d\n",find2(1,1,n,l,r));
	 		}
	 		else
	 		{
	 			printf("%d\n",find1(1,1,n,l,r)+find2(1,1,n,l,r));
	 		}
	 	}
	 }
	 return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值