题目
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;
}