題解 NOI2004【鬱悶的出納員】

\[ Preface \]c++

以前用 treap 打,交了四遍才過。spa

自學了 fhq treap 後,才意識到是一道 fhq treap 板子題,直接碼上,一遍就過。code

本題解提供的是 fhq treap 作法(感謝 fhq 神犇),不太瞭解 fhq treap 的同窗能夠去了解一下 fhq treap ,挺好理解的。(至少要了解兩種 split 和 merge 操做)
\[ Description \]
你須要維護一個可重集,支持 \(4\) 種操做:ip

I k 向集合內插入元素 \(k\)get

A k 將集合內全部元素加上 \(k\)it

S k 將集合內全部元素減去 \(k\)io

F k 查詢集合內第 \(k\) 大。class

一共 \(n\) 次操做。查詢

一開始給出一個下界 \(Minv\) ,表示集合內的全部元素必須大於等於 \(Minv\) ,在任什麼時候刻,小於 \(Minv\) 的全部元素會被馬上刪除。集合

最後還要輸出被刪除的元素個數。
\[ Solution \]
對於 I k 操做:

​ 首先先判斷一下 \(k\) 是否大於等於 \(Minv\) ,以後就是經典的插入操做了。

​ 讓原樹按 \(k-1\) 分裂成兩棵樹 \(x,y\) ,新建值爲 \(k\) 的節點 \(New\) ,合併 \(x,New,y\)

\(~\)

對於 A kS k 操做:

​ 考慮到 " AS 命令的總條數不超過 100 " ,想到了啥?暴力修改!

​ 顯然集合內的全部元素加上 \(k\) 或減去 \(k\),fhq treap 樹形態不變。

A k 操做就很好辦了,集合內的全部元素加上 \(k\) 當然仍是大於等於 \(Minv\) ,直接 \(dfs\) 暴力修改便可。

S k 操做以後還會致使一些元素小於 \(Minv\) ,這些元素在還沒操做前是小於 \(Minv+k\) 的。

​ 那麼咱們能夠將原樹按 \(Minv+k-1\) 分裂成兩棵樹 \(x,y\) 。樹 \(x\) 的全部元素在操做以後都是小於 \(Minv\) 的,那麼這次被刪除的元素個數即爲 \(size[x]\) ,將其累加進答案,隨後樹 \(x\) 就沒有用了,不用管它就好了。樹 \(y\) 的全部元素在操做以後都是大於等於 \(Minv\) 的,直接 \(dfs\) 暴力修改,最後將樹 \(y\) 當成原樹就好了。

\(~\)

對於 F k 操做:

​ 經典的查詢第 \(k\) 大。

​ 讓原樹按 \(size[root]-k\) 大小分裂成兩棵樹 \(x,y\) ,再讓樹 \(y\)\(1\) 大小分裂成兩棵樹 \(y,z\) ,此時樹 \(y\) 只有一個節點,這個節點就是咱們要找的第 \(k\) 大了,最後記得合併回去。

​ 固然,在 " 按大小分裂 " 上作一點小改動,或是用通常 treap 的查詢第 k 大也是能夠的。

\(~\)

至此,此題獲得完美解決。
\[ Code \]

#include<cstdio>
#include<cstdlib>
#include<algorithm>

#define RI register int

using namespace std;

inline int read()
{
    int x=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-f;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}

const int N=100100;

int m,Minv;

int tot,root;
struct treap{
    int lc,rc;
    int val,dat;
    int size;
}t[N];

int New(int val)
{
    tot++;
    t[tot].lc=t[tot].rc=0;
    t[tot].val=val;
    t[tot].dat=rand();
    t[tot].size=1;
    return tot;
}

void upd(int p)
{
    t[p].size=t[t[p].lc].size+t[t[p].rc].size+1; 
}

void split_v(int p,int val,int &x,int &y)
{
    if(!p)
        x=y=0;
    else
    {
        if(t[p].val<=val)
            x=p,split_v(t[p].rc,val,t[p].rc,y);
        else
            y=p,split_v(t[p].lc,val,x,t[p].lc);
        upd(p);
    }
}

void split_s(int p,int size,int &x,int &y)
{
    if(!p)
        x=y=0;
    else
    {
        if(t[t[p].lc].size<size)
            x=p,split_s(t[p].rc,size-t[t[p].lc].size-1,t[p].rc,y);
        else
            y=p,split_s(t[p].lc,size,x,t[p].lc);
        upd(p);
    }
}

int merge(int p,int q)
{
    if(!p||!q)
        return p^q;
    if(t[p].dat>t[q].dat)
    {
        t[p].rc=merge(t[p].rc,q),upd(p);
        return p;
    }
    else
    {
        t[q].lc=merge(p,t[q].lc),upd(q);
        return q;
    }
}

int x,y,z;

void insert(int val)
{
    split_v(root,val-1,x,y);
    root=New(val);
    root=merge(x,root);
    root=merge(root,y);
}

void dfs(int u,int val)
{
    if(!u)return;
    t[u].val+=val;
    dfs(t[u].lc,val),dfs(t[u].rc,val);
}

int GetValByRank(int rank)
{
    if(t[root].size<rank)
        return -1;
    split_s(root,t[root].size-rank,x,y);
    split_s(y,1,y,z);
    int ans=t[y].val;
    root=merge(x,y);
    root=merge(root,z);
    return ans;
}

int ans;

int main()
{
    m=read(),Minv=read();

    while(m--)
    {
        char op[2];scanf("%s",op);
        int val=read();

        switch(op[0])
        {
            case 'I':{
                if(Minv<=val)
                    insert(val);

                break;
            }

            case 'A':{
                dfs(root,val);

                break;
            }

            case 'S':{
                split_v(root,Minv+val-1,x,root);
                ans+=t[x].size;

                dfs(root,-val);

                break;
            }

            case 'F':{
                printf("%d\n",GetValByRank(val));

                break;
            }
        }
    }

    printf("%d\n",ans);

    return 0;
}

\[ Thanks \ for \ watching \]

相關文章
相關標籤/搜索