【参考代码】mz线段树基本操作训练

来源:互联网 发布:gbase 8a sql参考手册 编辑:程序博客网 时间:2024/05/29 11:07

QAQ为什么高一小朋友们的题都这么难。。

代码仅供参考,禁止照抄哦 = v =


说明一下:read()函数是读入优化,你们直接写scanf就行了。namespace只是为了封装,可以不要。

然后我线段树的姿势可能和你们老师讲的不太一样,个人觉得这样写起来简洁一些,而且效率高。


Promble A

原题是hdu1166。简单的点修改和区间查询。因为太水了我就写的树状数组 = =

#include <cstdio>#include <iostream>#include <cstring> #define lowbit(i) (i & -i)using namespace std;int read(){int sign = 1, n = 0; char c = getchar();while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }return sign*n;}const int Nmax = 50005;int N, a[Nmax];namespace BIT {int d[Nmax];inline void init() { memset(d + 1, 0, sizeof(d[0]) * N); }inline void Add(int pos, int x) {for (int i = pos; i <= N; i += lowbit(i)) d[i] += x;}inline int Query(int pos){int res = 0;for (int i = pos; i; i -= lowbit(i)) res += d[i];return res;}}int main(){for (int T = read(), cas = 1; T --; ++ cas) {N = read();for (int i = 1; i <= N; ++ i) a[i] = read();using namespace BIT; init();for (int i = 1; i <= N; ++ i) Add(i, a[i]);char s[10]; printf("Case %d:\n", cas);while (~scanf("%s", s) && s[0] != 'E') {int i = read(), j = read();if (s[0] == 'A') Add(i, j);else if (s[0] == 'S') Add(i, -j);else printf("%d\n", Query(j) - Query(i - 1));}}return 0;}


Problem B 

原题是hdu1754。记录最大值即可。

#include <cstdio>#include <iostream>#include <algorithm>#define lc (u << 1)#define rc (u << 1 | 1)using namespace std;int read(){int sign = 1, n = 0; char c = getchar();while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }return sign*n;}const int Nmax = 200005;int N, M;int a[Nmax];namespace SegmentTree {int mmax[Nmax << 2];inline void build(int u, int l, int r){if (l == r) {mmax[u] = a[l];return;}int mid = (l + r) >> 1;build(lc, l, mid); build(rc, mid + 1, r);mmax[u] = max(mmax[lc], mmax[rc]);}inline void update(int u, int l, int r, const int &pos, const int &x){if (l == r) {mmax[u] = x;return;}int mid = (l + r) >> 1;if (pos <= mid) update(lc, l, mid, pos, x);else update(rc, mid + 1, r, pos, x);mmax[u] = max(mmax[lc], mmax[rc]);}inline int Query(int u, int l, int r, int L, int R){if (l == L && r == R) return mmax[u];int mid = (l + r) >> 1;if (R <= mid) return Query(lc, l, mid, L, R);else if (L > mid) return Query(rc, mid + 1, r, L, R);return max(Query(lc, l, mid, L, mid), Query(rc, mid + 1, r, mid + 1, R));}}int main(){while (~scanf("%d%d", &N, &M)) {for (int i = 1; i <= N; ++ i) a[i] = read();using namespace SegmentTree;build(1, 1, N); char c; int A, B;while (M --) {scanf(" %c", &c); A = read(); B = read();if (c == 'Q') printf("%d\n", Query(1, 1, N, A, B));else update(1, 1, N, A, B);}}return 0;}

Problem C

原题hdu1698。记录当前区间的颜色即可,如果不止一种颜色,就标记为0。修改的时候记得下放标记。

#include <cstdio>#include <iostream>#define lc (u << 1)#define rc (u << 1 | 1)using namespace std;int read(){int sign = 1, n = 0; char c = getchar();while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }return sign*n;}const int Nmax = 100005;namespace SegmentTree {int lazy[Nmax << 2];void build(int u, int l, int r) {lazy[u] = 1; if (l == r) return; int mid = (l + r) >> 1;build(lc, l, mid); build(rc, mid + 1, r);} inline void Modify(int u, int l, int r, int L, int R, const int &x) {if (lazy[u] == x || (l == L && r == R)) { lazy[u] = x; return; }if (lazy[u]) { lazy[lc] = lazy[rc] = lazy[u]; lazy[u] = 0; }int mid = (l + r) >> 1;if (R <= mid) Modify(lc, l, mid, L, R, x);else if (L > mid) Modify(rc, mid + 1, r, L, R, x);else {Modify(lc, l, mid, L, mid, x);Modify(rc, mid + 1, r, mid + 1, R, x);}}inline int Query(int u, int l, int r) {if (lazy[u]) return (r - l + 1) * lazy[u];int mid = (l + r) >> 1;return Query(lc, l, mid) + Query(rc, mid + 1, r); }}int N, Q;int main(){for (int T = read(), cas = 1; T --; ++ cas) {N = read(), Q = read(); using namespace SegmentTree;build(1, 1, N);for (int l, r, x; Q --; ) {l = read(), r = read(), x = read();Modify(1, 1, N, l, r, x);}printf("Case %d: The total value of the hook is %d.\n", cas, Query(1, 1, N));}return 0;}

Problem D

原题hdu3308。记录左边连续上升的长度,右边连续上升的长度和整个区间连续上升的长度。
Pushup的时候合并一下就行了,然后Query横跨两个区间的时候也要考虑是否可以连续。

#include <cstdio>#include <iostream>#include <algorithm>using namespace std;int read(){int sign = 1, n = 0; char c = getchar();while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }return sign*n;}const int Nmax = 100005;int N, M;int a[Nmax];#define lc (u << 1)#define rc (u << 1 | 1)namespace SegmentTree {int lsum[Nmax << 2], rsum[Nmax << 2], msum[Nmax << 2];inline void Push_up(int u, int l, int r, int m){if (a[m] < a[m + 1]) {lsum[u] = (lsum[lc] == m - l + 1) ? (m - l + 1 + lsum[rc]) : lsum[lc];rsum[u] = (rsum[rc] == r - m) ? (r - m + rsum[lc]) : rsum[rc];msum[u] = max(max(msum[lc], msum[rc]), lsum[rc] + rsum[lc]);} else {lsum[u] = lsum[lc]; rsum[u] = rsum[rc];msum[u] = max(msum[lc], msum[rc]);} }void build(int u, int l, int r){if (l == r) {lsum[u] = rsum[u] = msum[u] = 1;return;}int mid = (l + r) >> 1;build(lc, l, mid); build(rc, mid + 1, r);Push_up(u, l, r, mid);}void Modify(int u, int l, int r, const int &pos, const int &x){if (l == r) { a[pos] = x; return; }int mid = (l + r) >> 1;if (pos <= mid) Modify(lc, l, mid, pos, x);else Modify(rc, mid + 1, r, pos, x);Push_up(u, l, r, mid);}int Query(int u, int l, int r, int L, int R) {if (L == l && R == r) return msum[u];int mid = (l + r) >> 1;if (R <= mid) return Query(lc, l, mid, L, R);if (L > mid) return Query(rc, mid + 1, r, L, R);int res = max(Query(lc, l, mid, L, mid), Query(rc, mid + 1, r, mid + 1, R));if (a[mid] < a[mid + 1]) res = max(res, min(mid - L + 1, rsum[lc]) + min(R - mid, lsum[rc]));return res;}}int main(){for (int T = read(); T --; ) {N = read(), M = read();for (int i = 1; i <= N; ++ i) a[i] = read();using namespace SegmentTree;build(1, 1, N); char s[10]; int a, b;while (M --) {scanf("%s", s); a = read(); b = read();if (s[0] == 'Q') printf("%d\n", Query(1, 1, N, a + 1, b + 1));else Modify(1, 1, N, a + 1, b);}}return 0;}


Problem E

原题hdu1542。扫描线入门。建议以后要准备省选&NOI的同学掌握。
具体做法请自行百度。建议自学,这种东西我估计老师也没法给你们讲懂的。
另外用到了离散化,这个也要先去学习一下。

#include <cstdio>#include <iostream>#include <algorithm>using namespace std;const int Lmax = 205;int N;struct Line {double x, down, up;int cover;bool operator < (const Line &b) const { return x < b.x; }}L[Lmax];double h[Lmax];#define lc (u << 1)#define rc (u << 1 | 1)namespace SegmentTree {int cover[Lmax << 2]; double len[Lmax << 2];inline void build(int u, int l, int r) {cover[u] = len[u] = 0;if (l == r) return;int mid = (l + r) >> 1;build(lc, l, mid); build(rc, mid + 1, r);}inline void Push_up(int u, int l, int r){if (cover[u]) len[u] = h[r] - h[l - 1];else len[u] = (l == r) ? 0 : len[lc] + len[rc];}void Update(int u, int l, int r, int L, int R, const int &x){if (l == L && r == R) {cover[u] += x;Push_up(u, l, r); return;} int mid = (l + r) >> 1;if (R <= mid) Update(lc, l, mid, L, R, x);else if (L > mid) Update(rc, mid + 1, r, L, R, x);else {Update(lc, l, mid, L, mid, x);Update(rc, mid + 1, r, mid + 1, R, x);}Push_up(u, l, r);}}int main(){ios :: sync_with_stdio(false);int cas = 0, M; while (cin >> N && N) {M = 0; double x1, x2, y1, y2;for (int i = 1; i <= N; ++ i) {cin >> x1 >> y1 >> x2 >> y2;h[M] = y1;L[M ++] = (Line) { x1, y1, y2, 1 };h[M] = y2;L[M ++] = (Line) { x2, y1, y2, -1 };}sort(h, h + M);N = unique(h, h + M) - h; sort(L, L + M); -- M;printf("Test case #%d\n", ++cas);using namespace SegmentTree; double ans = 0. ; build(1, 1, N - 1);for (int i = 0; i < M; ++ i) {int l = lower_bound(h, h + N, L[i].down) - h + 1;int r = lower_bound(h, h + N, L[i].up) - h;Update(1, 1, N - 1, l, r, L[i].cover);ans += len[1] * (L[i + 1].x - L[i].x);}printf("Total explored area: %0.2f\n\n", ans);}return 0;}


0 0