利用了二进制,二分的思想的一个很巧妙的数据结构,一个lowbit(x):二进制表示下的最右边的一个1开始对应的数值。
那么如果一个节点的为x左孩子,父亲节点就是 x + lowbit(x),如果是右孩子,父亲节点是 x-lowbit(x);
图中白条部分就是辅助数组C对应的最底下的和。
1、那么一个前缀和有是怎样的呢?
就是从最底下开始,边往上走,边往左走。
2、修改单点呢?
从最底下开始,边往上走,边往下走。
LA2191:
题目链接:https://vjudge.net/contest/147973#problem/A
题意:
先给出一个数组,然后有两个操作
S x y 把第x个数改成y
M x y计算x~y个数的和
Source Code:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 inline int lowbit(int x) 6 { 7 return x&-x; 8 } 9 10 struct FenwickTree 11 { 12 int n; 13 vector<int> C; 14 15 void resize(int n) 16 { 17 this->n = n; 18 C.resize(n+10); 19 } 20 void clear() 21 { 22 fill(C.begin(), C.end(), 0); 23 } 24 25 // 计算A[1]+A[2]+...+A[x] (x<=n) 26 int sum(int x) 27 { 28 int ret = 0; 29 while(x > 0) 30 { 31 ret += C[x]; 32 x -= lowbit(x); 33 } 34 return ret; 35 } 36 37 // A[x] += d (1<=x<=n) 38 void add(int x, int d) 39 { 40 while(x <= n) 41 { 42 C[x] += d; 43 x += lowbit(x); 44 } 45 } 46 }; 47 48 FenwickTree f; 49 const int maxn = 200000+5; 50 int a[maxn]; 51 52 int main() 53 { 54 // freopen("in.txt","r",stdin); 55 int n; 56 int cases = 0; 57 while(scanf("%d",&n),n) 58 { 59 if(cases>0) puts(""); 60 printf("Case %d: ",++cases); 61 f.resize(n); 62 f.clear(); 63 for(int i=1; i<=n; i++) 64 { 65 scanf("%d",&a[i]); 66 f.add(i,a[i]); 67 } 68 69 char op[5]; 70 while(scanf("%s",op)!=EOF) 71 { 72 if(!strcmp(op,"END")) 73 break; 74 if(op[0]=='M') 75 { 76 int x,y; 77 scanf("%d%d",&x,&y); 78 printf("%d ",f.sum(y)-f.sum(x-1)); 79 } 80 if(op[0]=='S') 81 { 82 int x,y; 83 scanf("%d%d",&x,&y); 84 int add = y - a[x]; 85 a[x] = y; 86 f.add(x,add); 87 } 88 } 89 } 90 91 return 0; 92 }
LA5902:
题目链接:https://vjudge.net/contest/147973#problem/B
题意:
XXX喜欢看电影,他有好多好多的影碟,每个影碟都有个独立的编号。开始是从下往上影碟的顺序是n~1,他每次拿出影碟的时候,你需要输出压在该影碟上的有几个。(拿出后其他影碟顺序不变)看完影碟后,XXX会把影碟放在最上面。
分析:
把每个影碟放到一个考后的位置,这个位置有影碟,那么这里就是 1,否则是 0 ,每次询问,就是这个影碟所在的位置之前的前缀和,
把他放到前面去,当前位置置为 0 ,top 的位置 加 1,注意要记录每个影碟的所在位置。
Source Code:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 6 inline int lowbit(int x) 7 { 8 return x&-x; 9 } 10 11 struct FenwickTree 12 { 13 int n; 14 vector<int> C; 15 16 void resize(int n) 17 { 18 this->n = n; 19 C.resize(n); 20 } 21 void clear() 22 { 23 fill(C.begin(), C.end(), 0); 24 } 25 26 // 计算A[1]+A[2]+...+A[x] (x<=n) 27 int sum(int x) 28 { 29 int ret = 0; 30 while(x > 0) 31 { 32 ret += C[x]; 33 x -= lowbit(x); 34 } 35 return ret; 36 } 37 38 // A[x] += d (1<=x<=n) 39 void add(int x, int d) 40 { 41 while(x <= n) 42 { 43 C[x] += d; 44 x += lowbit(x); 45 } 46 } 47 }; 48 49 FenwickTree f; 50 const int maxn = 100000+5; 51 int pos[maxn]; 52 53 54 int main() 55 { 56 //freopen("in.txt","r",stdin); 57 //freopen("out.txt","w",stdout); 58 int t; 59 scanf("%d",&t); 60 61 while(t--) 62 { 63 int n,q; 64 scanf("%d%d",&n,&q); 65 f.resize(maxn*2); 66 f.clear(); 67 68 for(int i=1; i<=n; i++) 69 { 70 f.add(maxn+i,1); 71 pos[i] = maxn+i; 72 } 73 74 int top = maxn; 75 while(q--) 76 { 77 int x; 78 scanf("%d",&x); 79 int tmp = pos[x]; 80 if(q!=0) 81 printf("%d ",f.sum(tmp-1)); 82 else printf("%d",f.sum(tmp-1)); 83 f.add(tmp,-1); 84 f.add(top--,1); 85 pos[x] = top + 1; 86 } 87 puts(""); 88 } 89 return 0; 90 }
LA4329
题意:
一条大街上住着n个乒乓球爱好者,经常组织比赛。每个人都有一个技能值ai,每场比赛需要3个人:两名选手和一名裁判。规定裁判位置必须在两个选手的中间,而且技能值也必须在两个选手的中间,问一共能组织多少种比赛。
分析:
和上题类似,每个技能值有的话为 1 ,否则为 0 ,每一个点都可以做裁判,那么他能组织多少场比赛?
a1~ai-1 有 ci个比 ai 小,ai+1~an有di个比他小,乘法加法原理,ci(n-i-di) + di(i-ci-1)。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 6 inline int lowbit(int x) { 7 return x&-x; 8 } 9 10 struct FenwickTree { 11 int n; 12 vector<int> C; 13 14 void resize(int n) { 15 this->n = n; 16 C.resize(n); 17 } 18 19 void clear() { 20 fill(C.begin(),C.end(),0); 21 } 22 23 //计算A[1] + A[2] + ... +A[n] 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 33 // A[x] +=d; 34 void add(int x,int d) { 35 while(x<=n) { 36 C[x] +=d; 37 x +=lowbit(x); 38 } 39 } 40 }; 41 42 const int maxn = 20000 + 5; 43 int n; 44 int a[maxn]; 45 FenwickTree f; 46 int c[maxn],d[maxn]; 47 48 int main() 49 { 50 int t; 51 scanf("%d",&t); 52 while(t--) 53 { 54 int maxa = 0; 55 scanf("%d",&n); 56 for(int i=1;i<=n;i++) { 57 scanf("%d",&a[i]); 58 maxa = max(maxa,a[i]); 59 } 60 61 f.resize(maxa); 62 f.clear(); 63 64 for(int i=1;i<=n;i++) { 65 f.add(a[i],1); 66 c[i] = f.sum(a[i]-1); 67 } 68 69 f.clear(); 70 for(int i=n;i>=1;i--) { 71 f.add(a[i],1); 72 d[i] = f.sum(a[i]-1); 73 } 74 75 long long ans = 0; 76 for(int i=1;i<=n;i++) { 77 ans +=(long long)c[i]*(n-i-d[i]) + (long long)(i-c[i]-1)*d[i]; 78 } 79 printf("%lld ",ans); 80 } 81 82 return 0; 83 }