洛谷P3822/LOJ2302/UOJ314/BZOJ4942[NOI2017]整数

首先要知道一个结论:已知一个二进制计数器,每次加一,暴力置位,均摊时间是$O(1)$的。

相似地,用会计分析或势能分析可以证明一个更强的结论:每次在计数器某一位上加一,复杂度仍为$O(1)$。所以,设数总长为$L$,若$a$始终不小于零,则我们得到一个$O(Lloga)$的方法。

但这道题要求支持减法(即回撤),这将使均摊失效,也就是单次加/减将是$O(L)$的。所以我们只能考虑把加减分开,保存$a$和$b$,加时在$a$上加,减时在$b$上加,查询时在$a-b$上查询。

那么考虑如何快速求出$a-b$的第k位。不难发现对第k位有影响的只有$a$,$b$的第k位和$a$,$b$更低位数的大小关系(决定是否借位),而大小关系由第一个不同位决定,可以考虑用set维护不同位,就可以做到$O(logL)$。

然而$L$可以达到$3\times 10^7$。。。所以用压位,$2^{64}$进制,unsigned long long自然溢出实现效率很高。此时每次加减可以视为只影响一位,复杂度为操作set的$O(logL)$,总复杂度$O(LlogL)$,由于$L$压位后小于$5\times 10^5$,效率很高。

#include<cstdio>
#include<set>
using namespace std;
typedef unsigned long long ull;
const int N=500050;
char rB[1<<21],*rS,*rT,wB[1<<21];
int wp=-1;
inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<21,stdin),rS==rT)?EOF:*rS++;}
inline void flush(){fwrite(wB,1,wp+1,stdout);wp=-1;}
inline void pc(char c){if(wp+1==(1<<21))flush();wB[++wp]=c;}
inline int rd(){
    char c=gc();
    bool f=0;
    for(;c<48||c>57;c=gc())if(c=='-')f=1;
    int x=c&15;
    for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15);
    return f?-x:x;
}
ull a[N],b[N];
set<int> S;
inline void add(ull *a,ull *b,int x,int k){
    int p=k>>6,pp=k&63;
    ull t=(ull)x>>63ull-pp,pre=a[p];t>>=1ull;
    a[p]+=(ull)x<<pp;if(a[p]<pre)++t;  //若a[p]<pre,则说明进位发生
    if(a[p]^b[p])S.insert(p);
    else if(S.find(p)!=S.end())S.erase(p);
    for(++p;t;++p){
        pre=a[p];a[p]+=t;t=a[p]<pre;
        if(a[p]^b[p])S.insert(p);
        else if(S.find(p)!=S.end())S.erase(p);
    }
}
inline bool query(int k){
    int p=k>>6,pp=k&63;
    bool ans=((a[p]>>pp)^(b[p]>>pp))&1ull;
    ull t1=a[p]&(1ull<<pp)-1ull,t2=b[p]&(1ull<<pp)-1ull;
    if(t1<t2)return ans^1;
    if(t1>t2||S.empty()||p<=*(S.begin()))return ans;
    set<int>::iterator it=S.lower_bound(p);--it;
    return ans^(a[*it]<b[*it]);
}
int main(){
    int n=rd(),t,x,y;
    rd();rd();rd();
    while(n--){
        t=rd();x=rd();
        if(t==1){
            y=rd();
            if(x>0)add(a,b,x,y);
            else if(x<0)add(b,a,-x,y);
        }else if(t==2){pc(query(x)?'1':'0');pc('\n');}
    }
    flush();
    return 0;
}
View Code

 

posted @ 2019-08-06 15:07  wangyuchen  阅读(128)  评论(0编辑  收藏  举报