Fable

题目大意

对一个序列做k轮冒泡排序,输出最终序列。

做法

尝试计算每一个数的位移。
计算一个数前进的位数t等于在它前面比它大的数个数与k取min。
同样前面最大的t个数都会后退一格。
于是splay模拟这个过程统计每个位置的位移即可。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=200000+10;
int father[maxn],a[maxn],b[maxn],c[maxn],size[maxn],tree[maxn][2],ad[maxn],sta[maxn];
int i,j,k,l,t,n,m,tot,top,root;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int pd(int x){
    return tree[father[x]][1]==x;
}
void update(int x){
    size[x]=size[tree[x][0]]+size[tree[x][1]]+1;
}
void rotate(int x){
    int y=father[x],z=pd(x);
    father[x]=father[y];
    if (father[y]) tree[father[y]][pd(y)]=x;
    tree[y][z]=tree[x][1-z];
    if (tree[x][1-z]) father[tree[x][1-z]]=y;
    tree[x][1-z]=y;
    father[y]=x;
    update(y);
    update(x);
}
void mark(int x,int v){
    if (!x) return;
    ad[x]+=v;
}
void clear(int x){
    if (ad[x]){
        mark(tree[x][0],ad[x]);
        mark(tree[x][1],ad[x]);
        c[x]+=ad[x];
        ad[x]=0;
    }
}
void remove(int x,int y){
    top=0;
    while (x!=y){
        sta[++top]=x;
        x=father[x];
    }
    while (top) clear(sta[top--]);
}
void splay(int x,int y){
    remove(x,y);
    while (father[x]!=y){
        if (father[father[x]]!=y)
            if (pd(x)==pd(father[x])) rotate(father[x]);else rotate(x);
        rotate(x);
    }
}
void insert(int &x,int y){
    if (!x){
        x=++tot;
        update(x);
        return;
    }
    if (a[y]<a[x]||a[y]==a[x]&&y<x){
        insert(tree[x][0],y);
        father[tree[x][0]]=x;
    }
    else{
        insert(tree[x][1],y);
        father[tree[x][1]]=x;
    }
    update(x);
}
int kth(int x,int y){
    clear(x);
    if (size[tree[x][0]]+1==y) return x;
    if (size[tree[x][0]]+1<y) return kth(tree[x][1],y-size[tree[x][0]]-1);
    else return kth(tree[x][0],y);
}
int rank(int x,int y){
    if (!x) return 0;
    clear(x);
    if (a[x]<a[y]||a[x]==a[y]&&x<y) return rank(tree[x][1],y);
    else return rank(tree[x][0],y)+size[tree[x][1]]+1;
}
int merge(int a,int b){
    if (!a||!b) return a+b;
    int j=kth(a,size[a]);
    splay(j,0);
    tree[j][1]=b;
    father[b]=j;
    update(j);
    return j;
}
void write(int x){
    if (!x){
        putchar('0');
        putchar('\n');
        return;
    }
    top=0;
    while (x){
        sta[++top]=x%10;
        x/=10;
    }
    while (top) putchar('0'+sta[top--]);
    putchar('\n');
}
int main(){
    freopen("fable.in","r",stdin);freopen("fable.out","w",stdout);
    n=read();k=read();
    fo(i,1,n){
        a[i]=read();
        insert(root,i);
        splay(i,0);
        root=i;
    }
    fd(i,n,1){
        splay(i,0);
        j=tree[i][0];l=tree[i][1];
        tree[i][0]=tree[i][1]=0;
        father[j]=father[l]=0;
        root=merge(j,l);
        t=rank(root,i);
        t=min(t,k);
        c[i]-=t;
        if (!t) continue;
        if (i-1==t) mark(root,1);
        else{
            j=kth(root,i-t-1);
            splay(j,0);
            mark(tree[j][1],1);
            root=j;
        }
    }
    fo(i,1,n) b[i+c[i]]=a[i];
    fo(i,1,n) write(b[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值