题目:点击打开题目链接
Problem Description:
很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。
这让很多学生很反感。
不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。
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
思路:这道题可以用树状数组、线段树都可以,都是单点更新,区间求最大值的过程。
其中用树状数组方法时需要注意查询的过程,要活用查询操作。从R开始查询,然后通过R - lowbit [ R ] 进行一个区间一个区间的查询来减少时间复杂度,但是如果要查询的区间的左界比L小,就只对此单点进行查询,再进行下一个区间的查询,直到查询到L的位置。
下面是2种方法的代码。
My DaiMa(树状数组):
#include<iostream>
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
int a[200005],build[200005],lowbit[200005],n;
//lowbit[i]表示i所管辖的数有多少个,也是i的二进制中从右往左第一个1的位置
void GetLowbit()
{
for(int i = 1; i <= n ; i++)
lowbit[i] = i&(-i);//-x为x的补码
}
///建树,先求出每一段的初始最大值存在build数组里
void TreeBuild()
{
for(int i = 1; i <= n; i++)
{
build[i] = a[i];
for(int j = i-1; j > i-lowbit[i]; j--)
build[i] = max(build[i],a[j]);
}
}
///单点更新,更新所有管辖到x的build值
void update(int x, int y)
{
a[x] = y;
for(int i = x; i <= n; i += lowbit[i])
build[i] = max(build[i],a[x]);
}
///查询
int Find_max(int x,int y)
{
int maxx = a[y];///从区间右边开始
while(y != x)
{
for(y -= 1; y-lowbit[y] > x; y -= lowbit[y])///根据lowbit进行一个区间一个区间的查询
maxx = max(maxx,build[y]);
maxx = max(maxx,a[y]);///当查询的区间的左界小于x时,只对单点进行查询
}
return maxx;
}
int main()
{
int m;
char ch;
while(cin >> n >> m)
{
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
GetLowbit();
TreeBuild();
int x,y;
while(m--)
{
cin >> ch >> x >> y;
if(ch == 'U') update(x,y);///单点更新
else cout << Find_max(x,y) << endl;///查询区间最大值
}
}
}
My DaiMa(线段树):
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<algorithm>
#include<queue>
#include<math.h>
#include<set>
#include<time.h>
#pragma GCC optimize(2)
using namespace std;
#define INF 1e9
typedef long long ll;
typedef unsigned long long ull;
const int N = 200000;
struct Node
{
int left,right,data;
}node[4*N];
///更新当前节点
void push_up(int root)
{
node[root].data = max(node[root<<1].data , node[root<<1|1].data);
}
///构建线段树
void build(int root,int left,int right)
{
node[root].left = left;
node[root].right = right;
if(left == right)
{
scanf("%d",&node[root].data);
return;
}
int mid = (left+right)/2;
build(root<<1,left,mid);///构建左子树
build(root<<1|1,mid+1,right);///构建右子树
push_up(root);
}
///单点更新
void update(int root,int a,int b)
{
if(node[root].left == node[root].right && node[root].left == a)
{
node[root].data = b;
return;
}
int mid = (node[root].left+node[root].right)/2;
if(a <= mid) update(root<<1 , a , b);///更新的点在左子树中
else update(root<<1|1 , a , b);///更新的点在右子树中
push_up(root);
}
///区间查询
int query(int l,int r,int root)
{
if(node[root].left == l && r == node[root].right)
return node[root].data;
int mid = (node[root].left+node[root].right)/2;
if(r <= mid)///说明区间在左子树中
query(l , r , root<<1);
else if(mid+1 <= l)///说明区间在右子树中
query(l, r, root<<1|1);
else ///说明区间一部分在左子树中,一部分在右子树中,因此劈两半,两边都得查询,取最大值
{
int x = query(l, mid, root<<1);///注意这里是mid,而不是r,因为区间被劈了两半
int y = query(mid+1, r, root<<1|1);
return max(x,y);
}
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
build(1, 1, n);
char ch;
int a,b;
while(m--)
{
cin >> ch >> a >>b;
if(ch == 'U')
update(1, a, b);
else
{
int ans = query(a, b, 1);
printf("%d\n",ans);
}
}
}
}