树状数组
一、树状数组单点更新与区间查询模板
1 //求出最低位的1 2 int lowbit(int x) 3 { 4 return x & (-x); 5 } 6 7 //单点更新 8 void update(int x,int y,int n) 9 { 10 for(int i = x;i <= n;i += lowbit(i)) 11 c[i] += y; 12 } 13 14 //区间查询(求和) 15 int query(int x) 16 { 17 int ans = 0; 18 for(int i = x;i >= 1;i -= lowbit(i)) 19 ans += c[i]; 20 return ans; 21 }
模板例题:敌兵布阵
http://acm.hdu.edu.cn/showproblem.php?pid=1166
题意:T组测试用例,给出n个营地的人数,将会询问多次,输入一个字符串,如果是Query就输出i到j间的总人数,如果是Add则在第i个兵营增加j个人,如果是Sub则第i个兵营减少j人,如果是End则询问结束。
AC代码
1 #include <bits/stdc++.h> 2 #define INF 50000 3 4 using namespace std; 5 6 int t; 7 int n; 8 char ch[10]; 9 int c[4*INF + 1]; 10 11 //求出最低位的1 12 int lowbit(int x) 13 { 14 return x & (-x); 15 } 16 17 //单点更新 18 void update(int x,int y,int n) 19 { 20 for(int i = x;i <= n;i += lowbit(i)) 21 c[i] += y; 22 } 23 24 //区间查询 25 int query(int x) 26 { 27 int ans = 0; 28 for(int i = x;i >= 1;i -= lowbit(i)) 29 ans += c[i]; 30 return ans; 31 } 32 33 int main() 34 { 35 scanf("%d",&t); 36 for(int p = 1;p <= t;p++) 37 { 38 scanf("%d",&n); 39 memset(c,0,sizeof(c)); 40 for(int i = 1;i <= n;i++) 41 { 42 int x; 43 scanf("%d",&x); 44 update(i,x,n); 45 } 46 printf("Case %d:\n",p); 47 while(1) 48 { 49 int l,r; 50 int ans = 0; 51 scanf("%s",ch); 52 if(ch[0] == 'E') 53 break; 54 scanf("%d%d",&l,&r); 55 if(ch[0] == 'Q') 56 { 57 ans = query(r) - query(l - 1); 58 printf("%d\n",ans); 59 } 60 else if(ch[0] == 'A') 61 { 62 update(l,r,n); 63 } 64 else if(ch[0] == 'S') 65 { 66 update(l,-r,n); 67 } 68 } 69 } 70 return 0; 71 }
二、树状数组求逆序对
首先了解什么是逆序对:设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。
模板:
int lowbit(int x) { return x & -x; } void update(int x,int y) { for(int i = x;i <= n;i += lowbit(i)) a[i] += y; } int query(int x) { int ans = 0; for(int i = x;i >= 1;i -= lowbit(i)) ans += a[i]; return ans; }
模板例题:Minimum Inversion Number
http://acm.hdu.edu.cn/showproblem.php?pid=1394
题意:给出一个长度为n的数组,分别经过n次转换,让你求出n次转换中数组的逆序对的最小值;
转换演示:
a1, a2, ..., an-1, an (第一次不变)
a2, a3, ..., an, a1 (第二次)
a3, a4, ..., an, a1, a2 (第三次)
...
an, a1, a2, ..., an-1 (第n次)
AC代码:
1 #include <bits/stdc++.h> 2 #define inf 5000 3 4 using namespace std; 5 6 int n; 7 int a[inf * 4]; 8 int b[inf * 4]; 9 long long sum = 0; 10 long long ans = 0x7f7f7f7f; 11 12 int lowbit(int x) 13 { 14 return x & -x; 15 } 16 17 void update(int x,int y) 18 { 19 for(int i = x;i <= n;i += lowbit(i)) 20 a[i] += y; 21 } 22 23 int query(int x) 24 { 25 int ans = 0; 26 for(int i = x;i >= 1;i -= lowbit(i)) 27 ans += a[i]; 28 return ans; 29 } 30 31 int main() 32 { 33 while(scanf("%d",&n) != EOF) 34 { 35 memset(a,0,sizeof(a)); 36 sum = 0; 37 for(int i = 1;i <= n;i++) 38 { 39 scanf("%d",&b[i]); 40 b[i]++; 41 sum += query(n) - query(b[i]); 42 update(b[i],1); 43 } 44 ans = sum; 45 for(int i = 1;i <= n;i++) 46 { 47 sum += n - b[i] - (b[i] - 1); 48 if(sum < ans) 49 ans = sum; 50 } 51 printf("%lld\n",ans); 52 } 53 return 0; 54 }
三、树状数组求区间最大值模板
int lowbit(int x) { return x & -x; } void update(int x) { for(;x <= n;x += lowbit(x)) { a[x] = b[x]; for(int i = 1;i < lowbit(x);i += lowbit(i)) a[x] = max(a[x],a[x - i]); } } int query(int l,int r) { int ans = 0; while(r >= l) { ans = max(ans,b[r]); r--; for(;r - lowbit(r) >= l;r -= lowbit(r)) ans = max(ans,a[r]); } return ans; }
模板例题:I Hate It
http://acm.hdu.edu.cn/showproblem.php?pid=1754
题意:给出长度为n的数组,进行m次询问,输入字符为Q时,输出i到j区间的最大值,如果输入字符为U时,将第i位数修改为j
AC代码:
#include <bits/stdc++.h> #define INF 200001 using namespace std; int a[INF]; int b[INF]; int m,n; int l,r; int x; string s; int lowbit(int x) { return x & -x; } void update(int x) { for(;x <= n;x += lowbit(x)) { a[x] = b[x]; for(int i = 1;i < lowbit(x);i += lowbit(i)) a[x]=max(a[x],a[x - i]); } return ; } int query(int l,int r) { int ans = 0; while(r >= l) { ans = max(ans,b[r]); r--; for(;r - lowbit(r) >= l;r -= lowbit(r)) ans=max(ans,a[r]); } return ans; } int main() { while(scanf("%d",&n) != EOF) { scanf("%d\n",&m); memset(a,0,sizeof(a)); for(int i = 1;i <= n;i++) { scanf("%d",&b[i]); update(i); } while(m--) { cin >> s; if(s[0] == 'Q') { scanf("%d%d",&l,&r); printf("%d\n",query(l,r)); } else { scanf("%d%d",&l,&r); b[l] = r; update(l); } } } return 0; }