【Ynoi2010】y-fast trie

STL,平衡树。

题目大意:

给定一个集合和一个正整数 $C$,支持两个操作。

  1. 插入一个元素 $x$,保证 $x$ 原来不存在。
  2. 删除一个元素 $x$,保证 $x$ 原来存在。

每次操作完后,要求在集合中选出两个不同元素 $A,B$,使得 $(A+B)\bmod C$ 的值最大。输出最大值。

题解:

我们可以对 $x$ 进行取模,这样显然不会影响答案,但是会出现重复的情况。

假设我们选出的 $A\geq B$。

我们进行分类讨论:

  1. $A+B\geq C$,此时我们要最大化 $A+B-C$ 的值,显然选最大的两个最优。使用优先队列完成。

  2. $A+B\lt C$,且 $A\lt\lceil\frac C 2\rceil$。此时由于 $B\leq A$,所以无论如何选 $B$ 都不会达到 $C$。这种情况,我们只需要选两个小于 $\lceil\frac C 2\rceil$ 即可。也使用优先队列完成。

  3. $A+B\lt C$,且 $A\geq \lceil\frac C 2\rceil$。考虑对于一个 $A$,能够和它加起来不需要取模的数的范围为 $[0,C-1-A]$。不难发现,$A$ 越大,区间越小,且能产生的贡献都是一段前缀。

    我们显然要求贡献最大,所以每个 $A$ 所覆盖到的是一段连续的区间,这段区间里的数作为 $B$ 时,和当前的 $A$ 加起来取得最大贡献。

    新加一个数相当于插入一个区间,删除一个数相当于把一个区间和后面一个并起来。

    而且这些区间从左到右贡献递减。所以加入、删除一段区间可以看做一个区间加的操作。而我们要求的是全局最大值。

    由于可能作为 $B$ 的值只有 $n$ 个,所以可以使用动态开点线段树实现区间加、全局最大值。

    但由于本题要求线性空间,所以需要使用平衡树来代替线段树。

    单次修改的复杂度显然是 $O(\log n)$。

这里所有操作都是单次 $O(\log n)$。

总时间复杂度 $O(n\log n)$,空间复杂度 $O(n)$。

代码上,没有纯 STL 的做法好写。但是思路非常简单。

大概用棵 ODT 代替平衡树就可以纯 STL?

fhq treap 常数有点大,略卡常。

Update(2020.10.15):手写哈希表把 unordered_map 吊起来锤。

Code:

#include<set>
#include<random>
#include<algorithm>
#include<cstdio>
#include<cctype>
#include<ctime>
#include<queue>
using namespace std;
char buf[(int)1e7],*ss=buf;
inline int init(){buf[fread(buf,1,(int)1e7-1,stdin)]='\n';fclose(stdin);return 0;}
const int __START__=init();
inline int readint(){
    int d=0;
    while(!isdigit(*ss))++ss;
    while(isdigit(*ss))d=d*10+(*ss++^'0');
    return d;
}
char bwf[9000005],*ww=bwf;
int xx[12];
inline void print(int x){
    static int*q=xx;
    if(!x)*ww++='0';else{
        for(;x;x/=10)*q++=(x%10)^'0';
        while(q!=xx)*ww++=*--q;
    }
    *ww++='\n';
}
mt19937 mt(time(0));
struct Hash_Table{
    static const int M=4565473,md=M-2;
    int cnt[M],head[M],nxt[M],tot;
    int val[M];
    Hash_Table(){tot=0;}
    inline int&operator[](const int&k){
        const int x=k%md;
        for(int i=head[x];i;i=nxt[i])
            if(val[i]==k)return cnt[i];
        nxt[++tot]=head[x],head[x]=tot,val[tot]=k;
        return cnt[tot];
    }
}mp;
set<int>_1;
const int N=5e5+6;
int n,md,t0t,last;
struct DS{
    priority_queue<int>p,q;
    int sz;
    inline void insert(int x){p.push(x),++sz;}
    inline void erase(int x){q.push(x),--sz;}
    inline int ask(){
        if(sz<2)return 0;
        while(!q.empty()&&p.top()==q.top())p.pop(),q.pop();
        int u=p.top();p.pop();
        while(!q.empty()&&p.top()==q.top())p.pop(),q.pop();
        int v=p.top();p.push(u);
        return u+v<md?u+v:u+v-md;
    }
}_2,big;
namespace sbt{
    struct node{
        int val,mx,ls,rs,sz,tag,pos;
        unsigned ran;
    }d[N];
    int rt,nod;
    inline void add(int x,int v){d[x].val+=v,d[x].mx+=v,d[x].tag+=v;}
    inline void update(int x){
        const int ls=d[x].ls,rs=d[x].rs;
        d[x].mx=max({d[ls].mx,d[rs].mx,d[x].val});
        d[x].sz=d[ls].sz+d[rs].sz+1;
    }
    inline void pushdown(int x){
        if(int&q=d[x].tag){
            const int ls=d[x].ls,rs=d[x].rs;
            if(ls)add(ls,q);
            if(rs)add(rs,q);
            q=0;
        }
    }
    int merge(int x,int y){
        if(!x||!y)return x|y;
        pushdown(x),pushdown(y);
        if(d[x].ran<d[y].ran)
        {d[x].rs=merge(d[x].rs,y),update(x);return x;}
        d[y].ls=merge(x,d[y].ls),update(y);return y;
    }
    void split(int u,int k,int&x,int&y){
        if(!u)x=y=0;else
        if(d[pushdown(u),u].pos<=k)split(d[u].rs,k,d[u].rs,y),x=u,update(x);else
        split(d[u].ls,k,x,d[u].ls),y=u,update(y);
    }
    void modify(int l,int r,int v){
        int x,y,z;
        split(rt,l-1,x,y);
        split(y,r,y,z);
        if(y)add(y,v);
        rt=merge(merge(x,y),z);
    }
    void insert(int pos,int v){
        d[++nod]=(node){v,v,0,0,1,0,pos,mt()};
        int x,y;
        split(rt,pos,x,y);
        rt=merge(merge(x,nod),y);
    }
    void erase(int pos){
        int x,y,z;
        split(rt,pos-1,x,y);
        split(y,pos,y,z);
        rt=merge(x,z);
    }
}
int main(){
    sbt::d[0].mx=sbt::d[0].val=-2e9;
    n=readint(),md=readint();
    _1.insert(-md-1),_1.insert(md);
    while(n--){
        int op=readint(),x=(readint()^last)%md;
        if(op==1){
            ++t0t;
            big.insert(x);
            if(!mp[x]){
                if(x>=(md+1)/2){
                    auto it=_1.upper_bound(x);
                    int l=md-*it,r=md-1-x;
                    sbt::modify(l,r,x-*--it);
                    _1.insert(x);
                }else _2.insert(x);
                int pr=*--_1.upper_bound(md-1-x);
                sbt::insert(x,x+pr);
                mp[x]=1;
            }else{
                ++mp[x];
                if(x<(md+1)/2)_2.insert(x);
            }
        }else{
            --t0t;
            big.erase(x);
            if(mp[x]==1){
                --mp[x];
                sbt::erase(x);
                if(x>=(md+1)/2){
                    _1.erase(x);
                    auto it=_1.upper_bound(x);
                    int l=md-*it,r=md-1-x;
                    sbt::modify(l,r,*--it-x);
                }else _2.erase(x);
            }else{
                --mp[x];
                if(x<(md+1)/2)_2.erase(x);
            }
        }
        if(t0t<=1)*ww++='E',*ww++='E',*ww++='\n',last=0;else{
            last=max({sbt::d[sbt::rt].mx,_2.ask(),big.ask()});
            print(last);
        }
    }
    fwrite(bwf,ww-bwf,1,stdout);
    return 0;
}