51Nod1502 苹果曼和纸

153 篇文章 0 订阅
134 篇文章 0 订阅

题目点这里

漏写博客好多天了,赶快来补

这道题是一个裸的模拟题,注意到纸片总长度不会增加,所以用一个带翻转的splay来维护一下

每次将前半段翻转和后一段做加法合并就好了,均摊logn

#pragma GCC opitmize("O3")
#pragma G++ opitmize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100010
#define son(x) (s[f[x]][1]==x)
using namespace std;
int f[N],s[N][2],sz[N],v[N],t[N];
int n,m,cnt=0,rt=0,tp=0,stk[N],r[N];
inline int newnode(int p){
    ++cnt; sz[cnt]=1;
    t[cnt]=v[cnt]=p;
    return cnt;
}
inline int ps(int x){
    sz[x]=sz[s[x][0]]+sz[s[x][1]]+1;
    t[x]=t[s[x][0]]+v[x]+t[s[x][1]]; 
}
inline void reverse(int x){
    swap(s[x][0],s[x][1]);
    r[s[x][0]]^=1; r[s[x][1]]^=1; r[x]=0;
}
inline void rot(int x){
    int p=f[x],g=f[p],d=son(x);
    s[p][d]=s[x][!d]; f[s[p][d]]=p;
    s[x][!d]=p; f[p]=x; f[x]=g;
    if(g) s[g][p==s[g][1]]=x; ps(p); ps(x);
}
inline void splay(int x,int rt=0){
    for(int y=x;y;y=f[y]) stk[++tp]=y;
    for(;tp;--tp) if(r[stk[tp]]) reverse(stk[tp]);
    for(int p;(p=f[x])!=rt;rot(x))
        if(f[p]!=rt && son(x)==son(p)) rot(p);
    if(!rt) ::rt=x;
}
inline int select(int x,int k){
    for(int w;;){
        if(r[x]) reverse(x);
        w=sz[s[x][0]]+1;
        if(w==k) return x;
        if(k<w) x=s[x][0];
        else k-=w,x=s[x][1];
    }
}
inline int rank(int x){
    int r=sz[s[x][0]]+1;
    for(;x;x=f[x])
        if(son(x)) r+=sz[s[f[x]][0]]+1;
    return r;
}
inline int merge(int x,int y){
    if(sz[x]>sz[y]) swap(x,y);
    for(int i=1;sz[x];++i){
        splay(x=select(x,1));
        splay(y=select(y,i));
        v[y]+=v[x]; ps(y); x=s[x][1];
    }
    return y;
}
void print(int x){
    if(!x) return;
    print(s[x][0]);
    printf("[%d]",v[x]);
    print(s[x][1]);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int x,i=1;i<=n;++i){
        x=newnode(1);
        if(i-1) f[i-1]=x,s[x][0]=i-1,ps(x);
    }
    rt=cnt; int t1=newnode(0),t2=newnode(0); f[t1]=t2; s[t2][0]=t1;
    for(int o,x,y;m--;){
        scanf("%d%d",&o,&x);
        if(o==1){
            splay(x=select(rt,x));
            y=s[x][1]; s[x][1]=0; f[y]=0; ps(x); r[x]=1;
            rt=merge(x,y);
        } else {
            scanf("%d",&y);    
            s[t1][1]=rt; f[rt]=t1; ps(t1); ps(t2);
            splay(select(t2,++x));
            splay(select(rt,y+2),rt);
            printf("%d\n",t[s[s[rt][1]][0]]);
            splay(t2); splay(t1,rt); f[rt=s[t1][1]]=0; s[t1][1]=0;
        }
        //print(rt); puts("");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值