HDU 3670 神树状数组(THX 2 ID:Goldbach)
事实证明,我也可以把代码写的像屎一样。
这题的关键点是在于把更新操作离线化,对于每次更新,仅仅保存add的值,并不对数列进行修改。
这样就可以保护原数列的性质不变改变。
我们先把每一个数拆解成16个后辍
对于每一个后辍num[0...i],若num[i]==0,保存在bit[0][i]中,位置为int(num[0...i])
若num[i]==1,保存在bit[1][i]中,位置为int(num[0...i])
在查询与修改操作中,对于每一次修改C a,我们将add+=a
对于Q a,我们分情况讨论。
因为Q a仅仅被add[0...a]这一部分影响,所以我们只需要考虑后辍num[0...a]这一个分支
而int(num[0...a])属于[0,(1<<a)-1]这个区间
设tail==add%(1<<a)
所以当add[a]==1时,我们要找出大小为bit[1]分组中[(1<<a)-tail,1<<(a+1)]的数的数量
以及bit[0]分组中[0...(1<<a)-tail]的数量。
反之也是同样的道理。
原理是在加上这个数之后,此位为1的数,因为在a位进位而使1变0的数会被下一位/几位的进位继续填充为1
此位为0的数,加上a之后,后几位不会再进位。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //Result:2012-08-18 14:29:32 Accepted 3670 375MS 8536K 1226 B C++ Wizmann 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #include <iostream> 6 #include <algorithm> 7 using namespace std; 8 const int SIZE=(1<<16)+128,LEN=16; 9 struct BIT 10 { 11 int baum[SIZE]; 12 inline int lowbit(int x){return (x)&(-x);} 13 inline void init(){memset(baum,0,sizeof(baum));} 14 inline void add(int pos,int val){while(pos<SIZE){baum[pos]+=val;pos+=lowbit(pos);}} 15 inline int sum(int pos){int res=0;while(pos>0){res+=baum[pos];pos-=lowbit(pos);}return res;} 16 inline int sum(int a,int b){return sum(b)-sum(a-1);} 17 }bit[2][16]; 18 int main() 19 { 20 int n,tmp,cas=1; char cmd[5]; 21 long long ans,add; 22 while(scanf("%d",&n)!=EOF && n!=-1) 23 { 24 for(int i=0;i<2;i++) for(int j=0;j<16;j++) bit[i][j].init(); 25 for(int i=0;i<n;i++) 26 { 27 scanf("%d",&tmp); 28 for(int j=0;j<LEN;j++) bit[(tmp&(1<<j))==0?0:1][j].add(tmp%(1<<j)+1,1); 29 } 30 add=ans=0; 31 while(1) 32 { 33 scanf("%s",cmd); 34 if(*cmd=='C'){scanf("%d",&tmp);add+=tmp;} 35 else if(*cmd=='Q') 36 { 37 scanf("%d",&tmp); 38 int tail=add%(1<<tmp),ptr=(add&(1<<tmp))==0?0:1; 39 ans+=bit[ptr][tmp].sum((1<<tmp)-tail+1,SIZE-1)+bit[!ptr][tmp].sum(1,(1<<tmp)-tail); 40 } 41 else break; 42 } 43 cout<<"Case "<<cas++<<": "<<ans<<endl; 44 } 45 return 0; 46 }