这里直接放上写的学习代码,都是模板,目的是理解这个工具是干什么的(理解什么的在这里):
学习思路参考的这里
1、单点修改, 区间查询
1 /* 2 单点修改, 区间查询 3 5 5 4 1 5 4 2 3 5 1 1 3 6 2 2 5 7 1 3 -1 8 1 4 2 9 2 1 4 10 answer:14 11 16 12 */ 13 #include <bits/stdc++.h> 14 using namespace std; 15 16 const int N = 500005, M = 500000; 17 #define lowbit(x) (x&(-x)) 18 int c[N], n, m; 19 int sum(int x){ 20 int ret = 0; 21 while(x > 0){ 22 ret += c[x]; 23 x -= lowbit(x); 24 } 25 return ret; 26 } 27 void add(int x, int d){ 28 while(x <= n){ 29 c[x] += d; 30 x += lowbit(x); 31 } 32 } 33 int main(){ 34 // freopen("in.txt", "r", stdin); 35 scanf("%d%d", &n, &m); 36 for(int i=1; i<=n; i++){ 37 int d; 38 scanf("%d", &d); 39 add(i, d); 40 } 41 for(int i=0; i<m; i++){ 42 int op, x, y; 43 scanf("%d%d%d", &op, &x, &y); 44 if(op==1){ 45 add(x, y); 46 }else{ 47 printf("%d ", sum(y)-sum(x-1)); 48 } 49 } 50 return 0; 51 }
2、区间修改,单点查询
1 /* 2 区间修改, 单点查询 3 5 5 4 1 5 4 2 3 5 1 2 4 2 6 2 3 7 1 1 5 -1 8 1 3 5 7 9 2 4 10 answer:6 11 10 12 */ 13 #include <bits/stdc++.h> 14 using namespace std; 15 #define lowbit(x) (x&(-x)) 16 const int N = 500005, M = 500000; 17 int c[N], n, m; 18 void add(int x, int d){ 19 while(x <= n){ 20 c[x] += d; 21 x += lowbit(x); 22 } 23 } 24 int sum(int x){ 25 int ret = 0; 26 while(x > 0){ 27 ret += c[x]; 28 x -= lowbit(x); 29 } 30 return ret; 31 } 32 int main(){ 33 // freopen("in.txt", "r", stdin); 34 scanf("%d%d", &n, &m); 35 int x=0, y; 36 for(int i=1; i<=n; i++){ 37 scanf("%d", &y); 38 add(i, y-x); 39 x = y; 40 } 41 for(int i=0; i<m; i++){ 42 int op, k; 43 scanf("%d", &op); 44 if(op==1){ 45 scanf("%d%d%d", &x, &y, &k); 46 add(x, k); 47 add(y+1, -k); 48 }else{ 49 scanf("%d", &x); 50 printf("%d ", sum(x)); 51 } 52 } 53 return 0; 54 }
3、区间修改,区间查询( 这里差分原数组构造数组d,维护两个树状数组d[i]和d[i]*i )
1 /* 2 区间修改, 区间查询 3 3 4 1 5 2 6 3 7 2 8 1 2 3 2 9 2 2 3 10 answer: 9 11 */ 12 #include <bits/stdc++.h> 13 using namespace std; 14 #define lowbit(x) (x&(-x)) 15 const int N = 500005, M = 500000; 16 int c1[N], c2[N], n, m; 17 int sum(int x){ 18 int ret = 0; 19 while(x > 0){ 20 ret += (x+1)*c1[x]-c2[x]; 21 x -= lowbit(x); 22 } 23 return ret; 24 } 25 void add(int x, int d){ 26 while(x <= n){ 27 c1[x] += d; c2[x] += x*d; 28 x += lowbit(x); 29 } 30 } 31 int main(){ 32 freopen("in.txt", "r", stdin); 33 scanf("%d", &n); 34 int x=0, y; 35 for(int i=1; i<=n; i++){ 36 scanf("%d", &y); 37 add(i, y-x); 38 x = y; 39 } 40 scanf("%d", &m); 41 for(int i=0; i<m; i++){ 42 int op, k; 43 scanf("%d", &op); 44 if(op==1){ 45 scanf("%d%d%d", &x, &y, &k); 46 add(x, k); 47 add(y+1, -k); 48 }else if(op==2){ 49 scanf("%d%d", &x, &y); 50 printf("%d ", sum(y)-sum(x-1)); 51 } 52 } 53 return 0; 54 }
LA 4329 - Ping pong
题意:n个人,三人一对比赛,中间为裁判,技能值也在两人中间(大中小, 小中大两种),问一共能组织多少中比赛。
树状数组的题目需要构造前缀和(当然不一定只是和),这里假设ai到ai-1有ci个比ai小,则有i-1-ci个比ai大,假设ai+1到an中有di个比ai小,则有n-i-di个比ai大。乘法原理和加法原理得ci*(n-i-di)+di*(i-ci-1)种比赛。
构造前缀和:x[j]表示目前为止已经考虑过的所有ai中,是否存在一个ai=j(x[j]=0表示不存在,x[j]=1表示存在),则ci=x[1]+……+x[a[i]-1],前缀和出现。
初始x[i]=0,在计算ci时,需要设x[a[i]]=1,更新后缀和,再求前缀和sum(a[i] - 1)。
1 #include <cstdio> 2 #include <iostream> 3 #include <cstdlib> 4 #include <cmath> 5 #include <cctype> 6 #include <string> 7 #include <cstring> 8 #include <algorithm> 9 #include <stack> 10 #include <queue> 11 #include <set> 12 #include <map> 13 #include <ctime> 14 #include <vector> 15 using namespace std; 16 #define LL long long 17 #define lowbit(x) ((x)&(-x)) 18 #define mem(s) memset(s, 0, sizeof(s)) 19 const int inf = 0x3f3f3f3f; 20 const int maxn = 20010, maxa = 1e6+10; 21 int a[maxn], c[maxa], d[maxn], s[maxn], Max; 22 int sum(int x){ 23 int s = 0; 24 for(;x;x-=lowbit(x)) s+=c[x]; 25 return s; 26 } 27 void add(int x, int d){ 28 for(;x<=Max;x+=lowbit(x)) c[x]+=d; 29 } 30 int main() 31 { 32 freopen("in.txt", "r", stdin); 33 int T; 34 scanf("%d", &T); 35 while(T--){ 36 int n; 37 scanf("%d", &n); 38 Max = 0; 39 for(int i=1; i<=n; i++){ 40 scanf("%d", &a[i]); 41 Max = max(Max, a[i]); 42 } 43 mem(c); 44 for(int i=1; i<=n; i++){ 45 add(a[i], 1); 46 s[i] = sum(a[i]-1); 47 } 48 mem(c); 49 for(int i=n; i>=1; i--){ 50 add(a[i], 1); 51 d[i] = sum(a[i]-1); 52 } 53 LL cnt=0; 54 for(int i=2; i<=n; i++){ 55 cnt += (LL)(s[i]*(n-i-d[i])+(i-1-s[i])*d[i]); 56 } 57 printf("%lld ", cnt); 58 } 59 return 0; 60 }