##[$B$ - $I$ $Hate$ $It$ $HDU$ - $1754$](http://acm.hdu.edu.cn/showproblem.php?pid=1754) ### 一、题目描述 很多学校流行一种比较的习惯。老师们很喜欢询问,从 **某某** 到 **某某** 当中,分数最高的是多少。这让很多学生很反感。 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要 **更新某位同学** 的成绩。 **$Input$** 本题目包含多组测试,请处理到文件结束。 在每个测试的第一行,有两个正整数 $N$ 和 $M$ ( $0 #include #include using namespace std; const int N = 2e5 + 1000; // 快读 int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar(); } return x * f; } // zkw线段树 int d[N << 2]; int n; // n个叶子节点 int q; // q个操作,区间查询或单点修改 int m; // m是指所有父节点们的个数 //构建重口味树 void built() { memset(d, 0, sizeof d); for (m = 1; m <= n + 1;) m <<= 1; // 强行开够(大于n)方便二进制访问叶节点 for (int i = m + 1; i <= m + n; i++) d[i] = read(); // n个同学的初始成绩,叶子节点 /*Q:理论上从m开始都是叶节点那为什么不从m开始而是m+1呢? A:因为在区间查询时最大是[1,n]而这个线段树区间查询需要变成开区间,即变为(0,n+1) 所以当然要给0的位置留个空区间查询 */ // 父亲们倒序逐个更新,是O(N) for (int x = m - 1; x; x--) d[x] = max(d[x << 1], d[x << 1 | 1]); } //单点修改 void modify(int x, int c) { //定点更新+向上统计更新,牛B啊~ //这个更新与上面的父亲们逐个更新相比,是单条路径的、爬树的更新过程,O(logN) for (d[x = x + m] = c, x >>= 1; x; x >>= 1) d[x] = max(d[x << 1], d[x << 1 | 1]); } //区间查询 int query(int l, int r) { int ans = -1; // 闭区间改为开区间:l=l+m-1 : 将查询区间改为l-1, r=r+m+1 : 将查询区间改为r+1 // l^r^1 : 相当于判断l与r是否是兄弟节点 for (l = l + m - 1, r = r + m + 1; l ^ r ^ 1; l >>= 1, r >>= 1) { if (~l & 1) ans = max(ans, d[l ^ 1]); // l % 2 == 0 左端点 是左儿子 max(右子树),想像一下题解中0号端点(哨兵),肯定是%2=0 if (r & 1) ans = max(ans, d[r ^ 1]); // r % 2 == 1 右端点 是右儿子 max(左子树),想像一下题解中1号端点,肯定是%2=1 } return ans; } int main() { //文件输入输出 #ifndef ONLINE_JUDGE freopen("HDU1754.in", "r", stdin); #endif char op[2]; while (~scanf("%d%d", &n, &q)) { //构建zkw树 built(); while (q--) { scanf("%s", op); int l = read(), r = read(); if (op[0] == 'Q') printf("%d\n", query(l, r)); else modify(l, r); } } return 0; } ```