1. 程式人生 > >【NOI2017】整數(壓位分塊+set)

【NOI2017】整數(壓位分塊+set)

【NOI2017】整數

題目簡述:
一開始你有一個數x=0
n(n106)次操作:
1.給x加上a×2k (a109,k3×107)
2.查詢x的二進位制表達表示2b(b3×107)的那位的值
根據最基本的均攤分析,我們知道只支援加的二進位制計數器均攤複雜度是O(1)
所以如果本題只支援加法的話可以利用unsignedint壓位
複雜度可以達到O(30n32)=O(n)
但是可能產生減法怎麼辦?
大力維護一個加法的結果
再維護一個減法的結果
查詢怎麼辦?
大力

比較字尾的大小即可
利用set記錄大小不同的塊的編號
所以這是道分塊題2333
總複雜度O(30n32log230n32)=O(nlog2n)
實際上根本跑不滿,
還是很快的

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
const int MAXN=1000010;
typedef unsigned long
long lsq; lsq Plus[MAXN],Minus[MAXN]; set<int>st; set<int>::iterator it; int n,t1,t2,t3,opt; int main() { scanf("%d%d%d%d",&n,&t1,&t2,&t3); while(n--) { scanf("%d",&opt); if(opt==1) { int a,b; scanf("%d%d",&a,&b); int
pos1=b/64,pos2=b%64; if(a>0) { lsq temp=Plus[pos1]; Plus[pos1]+=(lsq)(a<<pos2); lsq up=(lsq)(a>>(31-pos2)); up>>=31; up>>=2; up+=(Plus[pos1]<temp); if(Plus[pos1]^Minus[pos1]) st.insert(pos1); else if(st.count(pos1))st.erase(pos1); ++pos1; while(up) { temp=Plus[pos1]; Plus[pos1]+=up; up=(Plus[pos1]<temp); if(Plus[pos1]^Minus[pos1]) st.insert(pos1); else if(st.count(pos1))st.erase(pos1); ++pos1; } } else if(a<0) { a=-a; lsq temp=Minus[pos1]; Minus[pos1]+=(lsq)(a<<pos2); lsq up=(lsq)(a>>(31-pos2)); up>>=31; up>>=2; up+=(Minus[pos1]<temp); if(Plus[pos1]^Minus[pos1]) st.insert(pos1); else if(st.count(pos1))st.erase(pos1); ++pos1; while(up) { temp=Minus[pos1]; Minus[pos1]+=up; up=(Minus[pos1]<temp); if(Plus[pos1]^Minus[pos1]) st.insert(pos1); else if(st.count(pos1))st.erase(pos1); ++pos1; } } } else { int b; scanf("%d",&b); int pos1=b/64,pos2=b%64; int ans=(lsq)((Plus[pos1]>>pos2)^(Minus[pos1]>>pos2))&1; lsq valp=(lsq)Plus[pos1]%(1<<pos2),valm=(lsq)Minus[pos1]%(1<<pos2); if(valp<valm)printf("%d\n",(ans^1)); else if(valp>valm||(!st.size())||pos1<=*(st.begin())) printf("%d\n",ans); else { it=st.lower_bound(pos1); --it; if(Plus[*it]>Minus[*it])printf("%d\n",ans); else printf("%d\n",(ans^1)); } } } }