树状数组(用于每次只修改一个值的区间查询)
这里A[] 是原数组,C[] 就是树状数组了。
由演变过程来看,这里运用了二分的思想。设节点编号为x,那么这个节点所管理的区间长度为 2^k (k为x二进制末尾0的个数)。所以 Cn = A(n - 2^k + 1) + ... + An
感觉结合图跟模板不难懂
int n, m, a[MAXN], c[MAXN << 1]; //计算区间长度 2 ^ k int Lowbit(int x) { return x & (-x); } //修改 void change(int x, int num) { while(x <= n) { c[x] += num; x += Lowbit(x); } } //查询 int sum(int x) { int ans = 0; while(x > 0) { ans += c[x]; x -= Lowbit(x); } }
线段树
这篇详解
上图就是线段树的样子,给个简单的解释,例如这棵树是求区间和的话,那个 [1, 10]算的就是 [1, 10]的和
刚好是hdu1166
关于线段树的基本操作有建树,修改,查询
//建树,rt代表根,这里不懂的话手动结合上图模拟,从根开始 void Build(int l, int r, int rt) { if(l == r) { scanf("%d", &sum[rt]); return ; } int m = (l + r) >> 1; Build(l, m, rt << 1); Build(m + 1, r, rt << 1 | 1); //更新sum sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; }
//修改,设原数组为 a ,即为 a[x] += num void Update(int x, int num, int l, int r, int rt) { if(l == r) { sum[rt] += num; return ; } int m = (l + r) >> 1; //看 x 在左区间还是右区间 if(x <= m) Update(x, num, l, m, rt << 1); else Update(x, num, m + 1, r, rt << 1 | 1); sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; }
//查询 int Query(int L, int R, int l, int r, int rt) { if(L <= l && r <= R) return sum[rt]; int m = (l + r) >> 1; int ans = 0; if(L <= m) ans += Query(L, R, l, m, rt << 1);//左子区间与[L, R]有重叠 if(R > m) ans += Query(L, R, m + 1, r, rt << 1 | 1); return ans; }
上题题解
#include <iostream> #include <cstdio> #include <string> #include <cstring> #include <cmath> #include <sstream> #include <algorithm> #include <set> #include <map> #include <vector> #include <queue> #include <iomanip> #include <stack> using namespace std; typedef long long LL; const int INF = 0x3f3f3f3f; const int MAXN = 50005; const int MOD = 1e9 + 7; #define MemI(x) memset(x, -1, sizeof(x)) #define Mem0(x) memset(x, 0, sizeof(x)) #define MemM(x) memset(x, 0x3f, sizeof(x)) #define lson l, m, rt << 1 #define rson m + 1, r, rt << 1 | 1 int sum[MAXN << 2]; //建树,rt代表根,这里不懂的话手动结合上图模拟,从根开始 void Build(int l, int r, int rt) { if(l == r) { scanf("%d", &sum[rt]); return ; } int m = (l + r) >> 1; Build(l, m, rt << 1); Build(m + 1, r, rt << 1 | 1); //更新sum sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; } //修改,设原数组为 a ,即为 a[x] += num void Update(int x, int num, int l, int r, int rt) { if(l == r) { sum[rt] += num; return ; } int m = (l + r) >> 1; //看 x 在左区间还是右区间 if(x <= m) Update(x, num, l, m, rt << 1); else Update(x, num, m + 1, r, rt << 1 | 1); sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; } //查询 int Query(int L, int R, int l, int r, int rt) { if(L <= l && r <= R) return sum[rt]; int m = (l + r) >> 1; int ans = 0; if(L <= m) ans += Query(L, R, l, m, rt << 1);//左子区间与[L, R]有重叠 if(R > m) ans += Query(L, R, m + 1, r, rt << 1 | 1); return ans; } int main() { int T; scanf("%d", &T); for(int cas = 1;cas <= T;++cas) { printf("Case %d:\n", cas); int n; scanf("%d", &n); Build(1, n, 1); char s[15]; while(1) { scanf("%s", s); int x, y; if(s[0] == 'A') { scanf("%d%d", &x, &y); Update(x, y, 1, n, 1); } else if(s[0] == 'S') { scanf("%d%d", &x, &y); Update(x, -y, 1, n, 1); } else if(s[0] == 'Q') { scanf("%d%d", &x, &y); printf("%d\n", Query(x, y, 1, n, 1)); } else break; } } return 0; }
后续待更。。。