BZOJ 4942 NOI2017 整数 (压位+线段树)
题目大意:让你维护一个数x(x位数<=3*1e7),要支持加/减a*2^b,以及查询x的第i位在二进制下是0还是1
作为一道noi的题,非常考验写代码综合能力,敲+调+借鉴神犇的代码 3个多小时才过...
思路并不难,题目里b<=30n暗示压位,每次压30位可过
先分析一下加法,加a*2^b相当于在第b-1位加a,如果进位了(即>),那就在下一位+1,如果下一位还进位了,那就再下一位+1......
暴力进位显然不可取,那么用线段树维护它,维护连续的几大位之内是否全都是1
a<=1e9<=2^30,所以它最多被拆成两大位压进线段树里
那么每一个大位的加法操作可以变成 (方便描述,下面的数字是从左往右读):
1.当前位+=val,拆分出的val<=2^30
2.如果进位了,就去线段树里找接下来连续是1的最大的位置,然后在它下一位+1 比如01011 11111 10110,就是在第三位+1(实际操作可以直接找它下一位)
3.把这两位之间(不包括两端)的所有的大位都改成,区间修改打标记
减法操作和加法非常类似,借位就是找最右边是
查询操作非常简单没什么好说的,别忘了先下推标记再查询...
最先我的代码写了1个多小时,维护的东西一大堆,感觉恶心得一匹而且巨难调
实在是没想到好的优化方法,就借鉴了神犇gxzlegend的一些神级操作(orzorz):
a组(any)维护它子节点的&值,e数组(exist)维护它子节点的|值,这个维护方法很神,比我原来想的简单得多,而且不需要乱七八糟的打标记
find_inf和find_zero这两个函数也非常简洁明了
以及感谢LOJ的数据顺便吐槽一句辣鸡bzoj
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define ll long long 5 #define inf (1<<30)-1 6 #define il inline 7 #define ls rt<<1 8 #define rs rt<<1|1 9 #define N 1000100 10 using namespace std; 11 //re 12 int n,ma,t1,t2,t3; 13 int a[N<<2],e[N<<2],tag[N<<2]; 14 15 int gc() 16 { 17 int rett=0,fh=1;char p=getchar(); 18 while(p<'0'||p>'9'){if(p=='-')fh=-1;p=getchar();} 19 while(p>='0'&&p<='9'){rett=(rett<<3)+(rett<<1)+p-'0';p=getchar();} 20 return rett*fh; 21 } 22 il void pushup(int rt){ 23 e[rt]=e[rt<<1]|e[rt<<1|1]; 24 a[rt]=a[rt<<1]&a[rt<<1|1]; 25 } 26 il void pushdown(int rt){ 27 if(tag[rt]==-1) a[ls]=a[rs]=e[ls]=e[rs]=0,tag[rt]=0,tag[ls]=tag[rs]=-1; 28 if(tag[rt]==1) a[ls]=a[rs]=e[ls]=e[rs]=inf,tag[rt]=0,tag[ls]=tag[rs]=1; 29 } 30 int find_inf(int x,int l,int r,int rt) 31 { 32 if(a[rt]==inf) return -1; 33 if(l==r) return l; 34 pushdown(rt); 35 int mid=(l+r)>>1; 36 if(x<=mid){ 37 int pos=find_inf(x,l,mid,rt<<1); 38 if(pos==-1) return find_inf(x,mid+1,r,rt<<1|1); 39 else return pos; 40 } 41 return find_inf(x,mid+1,r,rt<<1|1); 42 } 43 int find_zero(int x,int l,int r,int rt) 44 { 45 if(!e[rt]) return -1; 46 if(l==r) return l; 47 pushdown(rt); 48 int mid=(l+r)>>1; 49 if(x<=mid){ 50 int pos=find_zero(x,l,mid,rt<<1); 51 if(pos==-1) return find_zero(x,mid+1,r,rt<<1|1); 52 else return pos; 53 } 54 return find_zero(x,mid+1,r,rt<<1|1); 55 } 56 int upd1(int x,int l,int r,int rt,int p) 57 { 58 if(l==r) 59 { 60 a[rt]+=p,e[rt]+=p; 61 if(a[rt]>inf){a[rt]-=(inf+1);e[rt]-=(inf+1);return 1;} 62 if(a[rt]<0){a[rt]+=(inf+1);e[rt]+=(inf+1);return -1;} 63 return 0; 64 } 65 pushdown(rt); 66 int mid=(l+r)>>1,ans=0; 67 if(x<=mid) ans=upd1(x,l,mid,rt<<1,p); 68 else ans=upd1(x,mid+1,r,rt<<1|1,p); 69 pushup(rt);return ans; 70 } 71 void upd2(int L,int R,int l,int r,int rt,int val) 72 { 73 if(L<=l&&r<=R){ 74 tag[rt]=(val)?1:-1; 75 a[rt]=e[rt]=(val)?inf:0; 76 return;} 77 pushdown(rt); 78 int mid=(l+r)>>1; 79 if(L<=mid) upd2(L,R,l,mid,rt<<1,val); 80 if(R>mid) upd2(L,R,mid+1,r,rt<<1|1,val); 81 pushup(rt); 82 } 83 void add(int x,int y) 84 { 85 int p=upd1(x,0,n,1,y); 86 if(!p) return; 87 int pos=find_inf(x+1,0,n,1); 88 if(pos-1>=x+1) upd2(x+1,pos-1,0,n,1,0); 89 upd1(pos,0,n,1,1); 90 } 91 void del(int x,int y) 92 { 93 int p=upd1(x,0,n,1,-y); 94 if(!p) return; 95 int pos=find_zero(x+1,0,n,1); 96 if(pos-1>=x+1) upd2(x+1,pos-1,0,n,1,1); 97 upd1(pos,0,n,1,-1); 98 } 99 int query(int x,int l,int r,int rt,int p) 100 { 101 if(l==r) return ((1<<p)&a[rt])?1:0; 102 int mid=(l+r)>>1; 103 pushdown(rt); 104 if(x<=mid) return query(x,l,mid,rt<<1,p); 105 else return query(x,mid+1,r,rt<<1|1,p); 106 } 107 int main() 108 { 109 scanf("%d%d%d%d",&n,&t1,&t2,&t3); 110 int fl,x,y; 111 for(int i=1;i<=n;i++) 112 { 113 fl=gc(); 114 if(fl==1){ 115 x=gc(),y=gc(); 116 if(x>=0){ 117 add(y/30,(x&((1<<(30-y%30))-1))<<(y%30)); 118 add(y/30+1,x>>(30-y%30)); 119 }else{ x=-x; 120 del(y/30,(x&((1<<(30-y%30))-1))<<(y%30)); 121 del(y/30+1,x>>(30-y%30)); 122 } 123 }else{ 124 x=gc(); 125 printf("%d\n",query(x/30,0,n,1,x%30)); 126 } 127 } 128 return 0; 129 }