Luogu-3391(スプレーテンプレート)

トピック

文学バランスツリー
https://www.luogu.org/problemnew/show/P3391
このプログラムはBZOJ-3223にも送信できます

分析

  • 初めてスプレイを書いたとき、プログラムにはダブルスピンとシングルスピンの2種類がありますが、luoguの時間は同じようです(ダブルスピンの方が遅いように見えても?)
  • もともと私はローテーションを書いたときに&を使用しませんでしたが、後にブログの投稿を見てこの手法でコードの量を効果的に削減できることを発見しました。
  • タグを取得して、この単語ツリーを反転する必要があることを示し、それをダウンロードすると、左右の息子を入れ替えることができます。
  • だらおがあれば、修正を手伝ってくれます、ありがとうございます!(他の人の時間を見ると、私のものよりもはるかに速いようです...)

プログラム

#include <cstdio>
#include <algorithm>
using namespace std;
int fa[100005],ls[100005],rs[100005],sz[100005],tag[100005],root,n,m;
void mer(int x){sz[x]=sz[ls[x]]+sz[rs[x]]+1;}

void bui(int l,int r,int Fa){   //建立一棵相对平衡的二叉树 
    if (l>r) return;
    int mid=l+r>>1;
    fa[mid]=Fa;
    if (mid>Fa) rs[Fa]=mid;else ls[Fa]=mid;
    bui(l,mid-1,mid); bui(mid+1,r,mid);
    mer(mid);
}

void node_up(int x){    //在不改变中序的前提下把节点 x 往上弄一层 
    int Fa=fa[x],L=ls[x],R=rs[x];
    if (Fa==ls[fa[Fa]]) ls[fa[Fa]]=x;else rs[fa[Fa]]=x;
    if (x==ls[Fa]){
        fa[x]=fa[Fa],fa[Fa]=x,fa[R]=Fa;
        rs[x]=Fa,ls[Fa]=R;
    }else{
        fa[x]=fa[Fa],fa[Fa]=x,fa[L]=Fa;
        ls[x]=Fa,rs[Fa]=L;
    }
    mer(x); mer(Fa);
    if (fa[x]==0) root=x;
}

void tag_add(int x){tag[x]^=1;}
void tag_down(int x){
    if (tag[x]){
        swap(ls[x],rs[x]);
        tag[ls[x]]^=1;
        tag[rs[x]]^=1;
        tag[x]=0;
    }
}

int find(int k){        //找到当前序列中第 k 个数 
    //由于多设了一个 0 节点,所以只需 cnt==k (中序遍历中左边有 k 个数)就是实际序列中第 k 个数 
    for (int x=root,cnt=-1; cnt!=k; ){
        tag_down(x);
        if (x==ls[fa[x]]) cnt-=sz[rs[x]]+1;
        else cnt+=sz[ls[x]]+1;
        if (cnt==k) return x;
        x=(cnt<k) ? rs[x]:ls[x];
    }
}

void To_(int x,int &goal){  //单旋 
    if (x==goal) return;
    while (goal!=x) node_up(x);
}
void To(int x,int &goal){   //双旋 
    while (goal!=x){
        if (fa[x]==goal) {node_up(x); break;}
        node_up(fa[x]);
        node_up(x);
    }
}

void dfs(int x){
    if (!x) return;
    tag_down(x);
    dfs(ls[x]); if (x>1 && x<=n+1) printf("%d ",x-1); dfs(rs[x]);
}

int main(){
    //freopen("1.txt","r",stdin);
    scanf("%d%d",&n,&m);
    root=n+3>>1; bui(1,n+2,0);      //加入节点 0 和 n+1,然后为了代码方便,把每个节点的键值加一 
    for (int l,r,L,R; m--; ){
        scanf("%d%d",&l,&r);
        L=find(l-1); To(L,root);    //把 (l-1) 弄到根节点上 
        R=find(r+1); To(R,rs[L]);   //把 (r+1) 弄到根节点的右儿子上 
        tag_add(ls[R]);             //打上翻转标记 
    }
    dfs(root);      //输出 
}

おすすめ

転載: blog.csdn.net/jackypigpig/article/details/78643730