ORZYYBc++
题目大意:你须要维护一个有$3\times 10^7$个二进制位的数,有一种修改方式和一种询问方式spa
对这个数加上$a\times2^b$,其中$|a|≤10^9$,$b≤3\times 10^7$,保证须要维护的这个数始终非负code
询问这个数第k个二进制位的值blog
总共有$10^6$次询问/修改操做it
咱们不难发现,若是只有加法操做的话,对任意一个位执行加法操做,均摊进位次数是1。class
证实是显然的(我貌似以前在MC里面用红石电路模拟过二进制进位过程。。。。)二进制
也就是说暴力加暴力进位的复杂度是正确的。im
可是这里有a并不保证非负,这样一来经过精心(大雾)的构造方式,可让你疯狂进位/退位,因此并不能单纯暴力模拟。查询
咱们考虑对加法部分和减法部分分开维护(设A为加法的部分,B为减法的部分),这样的进位复杂度显然就是对的。di
考虑到$A≥B$,那么显然有$\frac{A}{2^k}≥\frac{B}{2^k}$。
对于每次查询操做,咱们分别找出A的第k位和B的第k位
如今对答案会产生影响的显然是A的末k-1位和B的末k-1位相减后,A的第k位是否须要退位。
咱们考虑开一个set,若A的第i位和B的第i位不一样,那么咱们就把i丢入set中。
咱们考虑在set中找到知足<k的i,直接判断A的第i位和B的第i位的大小关系,就能够判出是否会产生退位。
set的维护在对大数作修改的时候去更新。
考虑到这个数很是大,直接维护会超时,咱们不妨作一波压位,而后再来维护,这样就跑得快不少了。
注意!对于一个32位的数,左移32位的操做会直接被忽略。
固然以前还有一些比较菜的想法,维护整个数的差分序列,若差分序列某个位置不为0就丢入set中,而后也来压位一波,不过代码估计长不少。
时间复杂度:$O(n\ log\ n)$
1 #include<bits/stdc++.h> 2 #define M 500005 3 #define S 64 4 #define L unsigned long long 5 using namespace std; 6 7 int n,t1,t2,t3; 8 L a[M]={0},b[M]={0},P=-1; 9 set<int> s; 10 11 void upd(int x){ 12 if(a[x]!=b[x]) s.insert(x); 13 else{ 14 if(s.find(x)!=s.end()) s.erase(x); 15 } 16 } 17 18 int main(){ 19 scanf("%d%d%d%d",&n,&t1,&t2,&t3); 20 while(n--){ 21 int op,B; scanf("%d",&op); L A; 22 int aa; 23 if(op==1){ 24 scanf("%d%d",&aa,&B); 25 if(aa>0){ 26 A=aa; 27 int x=B/S,y=B%S; 28 L s1=(A<<y)&P,s2=y?(A>>(S-y)):0; 29 a[x]=a[x]+s1; upd(x); 30 if(a[x]<s1) s2++; 31 while(s2){ 32 x++; 33 a[x]=a[x]+s2; upd(x); 34 s2=(a[x]<s2); 35 } 36 }else{ 37 A=-aa; 38 int x=B/S,y=B%S; 39 L s1=(A<<y)&P,s2=y?(A>>(S-y)):0; 40 b[x]=b[x]+s1; upd(x); 41 if(b[x]<s1) s2++; 42 while(s2){ 43 x++; 44 b[x]=b[x]+s2; upd(x); 45 s2=(b[x]<s2); 46 } 47 } 48 }else{ 49 scanf("%d",&B); 50 int x=B/S,y=B%S; 51 int ans=(((a[x]>>y)^(b[x]>>y))&1); 52 A=y?(a[x]<<(S-y)):0; 53 L BB=y?(b[x]<<(S-y)):0; 54 if(A<BB) ans^=1; 55 if(A==BB){ 56 set<int>::iterator it=s.lower_bound(x); 57 if(it!=s.begin()){ 58 it--; x=*it; 59 if(a[x]<b[x]) ans^=1; 60 } 61 } 62 printf("%d\n",ans); 63 } 64 } 65 }