线段树 + 树状数组 【小谈】
2017-07-24 09:13 tlnshuju 阅读(126) 评论(0) 编辑 收藏 举报
线段树和树状数组就学了以下一点,并且还是几个月前学的,说明这段时间数据结构没一点长进......还是先记录一下吧, 至于离散化,眼下做的题目较少,先不写了。还要学区间操作、扫描线......任重而道远。
问题一:给你N个数和M次操作,操作分两种——
1,U a b 第a个数添加b;2,S a b 求区间[a, b]的和。
这类问题树状数组能够非常简洁的实现:
#include <cstdio> #include <cstring> #include <algorithm> #define MAXN 100000+10 using namespace std; int sum[MAXN<<1]; int N, M; int lowbit(int x) { return x & (-x); } void update(int x, int d) { while(x <= N) { sum[x] += d; x += lowbit(x); } } int query(int x) { int ans = 0; while(x > 0) { ans += sum[x]; x -= lowbit(x); } return ans; } int main() { while(scanf("%d%d", &N, &M) != EOF) { memset(sum , 0, sizeof(sum)); int a, b; char op[10]; for(int i = 1; i <= N; i++) { scanf("%d", &a); update(i, a); } while(M--) { scanf("%s%d%d", op, &a, &b); if(op[0] == 'U') update(a, b); else printf("%d\n", query(b) - query(a-1)); } } return 0; }
问题二:给你N个数和M次操作。操作分三种——
1,U a b 第a个数变成b;2,S a b 求区间[a, b]的和;3。M a b 求区间[a, b]的最大值
线段树实现:
#include <cstdio> #include <cstring> #include <algorithm> #define MAXN 100000+10 using namespace std; int sum[MAXN<<2]; int Max[MAXN<<2]; void PushUp(int o) { sum[o] = sum[o<<1] + sum[o<<1|1]; Max[o] = max(Max[o<<1], Max[o<<1|1]); } void build(int o, int l, int r) { if(l == r) { scanf("%d", &sum[o]); Max[o] = sum[o]; return ; } int mid = (l + r) >> 1; build(o<<1, l, mid); build(o<<1|1, mid+1, r); PushUp(o); } void update(int o, int l, int r, int pos, int val) { if(l == r) { Max[o] = sum[o] = val;//若是添加 直接加上val值 return ; } int mid = (l + r) >> 1; if(pos <= mid) update(o<<1, l, mid, pos, val); else update(o<<1|1, mid+1, r, pos, val); PushUp(o); } int querysum(int o, int l, int r, int L, int R) { if(L <= l && R >= r) return sum[o]; int mid = (l + r) >> 1; int ans = 0; if(R <= mid) ans += querysum(o<<1, l, mid, L, R); else if(L > mid) ans += querysum(o<<1|1, mid+1, r, L, R); else { ans += querysum(o<<1, l, mid, L, mid); ans += querysum(o<<1|1, mid+1, r, mid+1, R); } return ans; } int querymax(int o, int l, int r, int L, int R) { if(L <= l && R >= r) return Max[o]; int mid = (l + r) >> 1; int ans = 0; if(R <= mid) ans = max(querymax(o<<1, l, mid, L, R), ans); else if(L > mid) ans = max(querymax(o<<1|1, mid+1, r, L, R), ans); else { ans = max(querymax(o<<1, l, mid, L, mid), ans); ans = max(querymax(o<<1|1, mid+1, r, mid+1, R), ans); } return ans; } int main() { int N, M; while(scanf("%d%d", &N, &M) != EOF) { build(1, 1, N); int a, b; char op[10]; while(M--) { scanf("%s%d%d", op, &a, &b); if(op[0] == 'U') update(1, 1, N, a, b); else if(op[0] == 'S') printf("%d\n", querysum(1, 1, N, a, b)); else printf("%d\n", querymax(1, 1, N, a, b)); } } return 0; }
问题三:给你N个数和M次操作,操作有两种——
1。A a b c 区间[a, b]全部数改动为c; 2。 Q a b 求区间[a, b]全部数之和。
#include <cstdio> #define MAX 100000+10 int col[MAX<<2]; int sum[MAX<<2]; void PushUp(int o) { sum[o] = sum[o<<1] + sum[o<<1|1]; } void PushDown(int o, int m) { if(col[o]) { col[o<<1] = col[o<<1|1] = col[o]; sum[o<<1] = (m - (m >> 1)) * col[o]; sum[o<<1|1] = (m >> 1) * col[o]; col[o] = 0; } } void build(int o, int l, int r)//建树 { col[o] = 0; if(l == r) { scanf("%d",&sum[o]); return ; } int mid = (l + r) >> 1; build(o<<1,l, mid); build(o<<1|1, mid+1, r); PushUp(o); } void update(int o, int l, int r, int L, int R, int v)//更新 { if(L <= l && r <= R) { col[o] = v; sum[o] = v * (r - l + 1); return ; } PushDown(o , r - l + 1); int mid = (l + r) >> 1; if (L <= mid) update(o<<1, l, mid, L , R , v); if (R > mid) update(o<<1|1, mid+1, r, L , R , v); PushUp(o); } int query(int o, int l, int r, int L, int R) //查询 { if(L <= l && R >= r) { return sum[o]; } PushDown(o, r-l+1); int mid = (r+l) >> 1; int res = 0; if(L <= mid) res += query(o<<1, l, mid, L, R); if(R > mid) res += query(o<<1|1, mid+1 , r, L, R); return res; } int main() { int N, M; int a, b, v; char op; while(scanf("%d%d", &N, &M)!=EOF) { build(1, 1, N); while(M--) { getchar(); scanf("%c", &op); if(op == 'A') { scanf("%d%d%d", &a, &b, &v);//把区间[a,b]全部数全部改为v update(1, 1, N, a, b, v); } else if(op == 'Q') { scanf("%d%d", &a, &b);//求区间[a,b]全部数之和 printf("%d\n", query(1, 1, N, a, b)); } } } return 0; }
问题四:给你N个数和M次操作,操作分两种——
1,C a b c 把区间[a, b]全部数全加c。 2。Q a b 求区间[a, b]全部数之和。
#include<cstdio> #include<cstring> #define MAX 100000+10 using namespace std; int sum[MAX<<2]; int add[MAX<<2]; void PushUp(int o) { sum[o] = sum[o<<1] + sum[o<<1|1]; } void PushDown(int o, int m) { if(add[o]) { add[o<<1] += add[o]; add[o<<1|1] += add[o]; sum[o<<1] += add[o] * (m-(m>>1)); sum[o<<1|1] += add[o] * (m>>1); add[o] = 0; } } void build(int o,int l,int r)//建树 { add[o] = 0; if(l == r) { scanf("%d",&sum[o]); return ; } int mid = (r+l) >> 1; build(o<<1, l, mid); build(o<<1|1, mid+1, r); PushUp(o); } void update(int o, int l, int r, int L, int R, int v)//更新 { if(L <= l && R >= r) { add[o] += v; sum[o] += v * (r-l+1); return ; } PushDown(o, r-l+1); int mid = (r+l) >> 1; if(L <= mid) update(o<<1, l, mid, L, R, v); if(R >mid) update(o<<1|1, mid+1, r, L, R, v); PushUp(o); } int query(int o, int l, int r, int L, int R)//查询 { if(L <= l && R >= r) { return sum[o]; } PushDown(o, r-l+1); int mid = (r+l) >> 1; int res = 0; if(L <= mid) res += query(o<<1, l, mid, L, R); if(R >mid) res += query(o<<1|1, mid+1, r, L, R); return res; } int main() { int N, M; int a, b, v; char op; while(scanf("%d%d", &N, &M), N||M) { build(1, 1, N); while(M--) { getchar(); scanf("%c", &op); if(op == 'C')//区间[a,b]每一个数自加 v { scanf("%d%d%d", &a, &b, &v); update(1, 1, N, a, b, v); } else if(op == 'Q')//区间[a,b]全部数之和 { scanf("%d%d", &a, &b); printf("%d\n", query(1, 1, N, a, b)); } } } return 0; }