/*I Hate It Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 41131 Accepted Submission(s): 16306 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 HintHuge input,the C function scanf() will work better than cin Author linle Source 2007省赛集训队练习赛(6)_linle专场 Recommend lcy | We have carefully selected several similar problems for you: 1698 1542 1394 2795 1540 */ #include<stdio.h> struct node { int l, r, g; }node[800100]; int fa[200022], Max; void buildtree(int i, int left, int right)//建造一个空的二叉树 { node[i].l = left; node[i].r = right; node[i].g = 0; //将树中的值全部初始化为0 if(left == right)//如果左右相等,说明已经到了二叉树的底部某个位置,可以存原始数组中的某个值 { fa[left] = i;//fa保存原始数组中的固定位置的值在二叉树中的位置 left即为原始数组中的第几个数i即为它在二叉树中的位置 return; } buildtree(i<<1, left, (left+right)/2);//分别两边建树 buildtree((i<<1)+1, (left+right)/2+1, right); } void update(int x)//将二叉树中x处的值更新为num { if(x == 1) return;//编号为1已经更新到根节点 int f = x>>1; //编号除2,求出父节点的编号 int a = node[f<<1].g; //求出该父节点的两个儿子的值 int b = node[(f<<1)+1].g; node[f].g = a>b?a:b;//父节点的值更新为其中较大的值 update(f); //递归往上更新 } void query(int i, int x, int y) { if(node[i].l == x && node[i].r == y)//如果区间找到,则比较最大值 { Max = Max > node[i].g?Max:node[i].g; return ; } i <<= 1;// 找出子节点 if(x <= node[i].r ) { if(y <= node[i].r)//子区间在左子树中 query(i, x, y); else query(i, x, node[i].r);//更改查询上限 } i++; if(y >= node[i].l) { if(x >= node[i].l)//区间包含 query(i, x, y); else query(i, node[i].l, y);//更改下限继续查询 } } int main() { int n, m, x, y, t; char ch; while(scanf("%d%d", &n, &m) != EOF) { buildtree(1, 1, n);//以1号节点为根节点建立二叉树 for(int i = 1; i <= n; i++) { scanf("%d", &t); node[fa[i]].g = t;//保存更改值 update(fa[i]);//更新二叉树 } while(m--) { getchar(); scanf("%c %d %d", &ch, &x, &y); if(ch == 'Q') { Max = 0; query(1, x,y); printf("%d\n",Max); } else { node[fa[x]].g = y;//更新线段树中的最底部的x处的值 update(fa[x]);//由于底部更新,则二叉树上面的全部更新 } } } } //树状数组超时代码 /*#include<stdio.h> #include<string.h> int a[200010], c[200010], n; void update(int x, int num, int n) { while(x <= n) { c[x] = c[x]>num?c[x]:num; x += x&(-x); } } int query(int x, int y) { int max = 0, j; j = y; while(j >= x) { if(j - j&(-j) >= x-1)//如果此数涵盖区间在查询区间内 寻找最大值 { max = max>c[j]?max:c[j]; j -= j&(-j); } else // 否则比较原数组中的单个值 { max = max>a[j]?max:a[j]; j--; } } return max; } int main() { int i, j, m; while(scanf("%d%d", &n, &m) != EOF) { memset(c, 0,sizeof(c)); for(i = 1; i <= n; i++) { scanf("%d", &a[i]); if(i&1) c[i] = a[i]; else update(i, a[i], n); } char ch; int max = 0; while(m--) { getchar(); scanf("%c %d %d", &ch, &i, &j); if(ch == 'U') { update(i, j, n); a[i] = j; } else { max = query(i,j); printf("%d\n", max); } } } return 0; }*/
题意:给出一序列数字,有两种操作,一时更改序列中的值,一个是查询一段区间内的最大值。输出查询时的最大值。
思路:标准的线段树模板题,代码中注释也比较详细了。
关键:需要弄清楚二叉树中的编号与左右区间的的关系,刚开始学容易弄混。