CF38G Queue(splay)

题目链接

https://codeforces.com/contest/38/problem/G

题意

有n个人依次排队,每个人都有两个属性值 a[i] 、c[i] ,a[i]是重要性值,数值越大越重要,c[i]是良心值。假如前i-1人已经排好队后,第i个人来排队,初始时他在队尾,如果他的a[i]大于排在他前面那位的重要性值,那么两人可以交换位置,每次交换良心值减1,直到他前面的人的重要性值大于a[i]或者良心值为0的时候(即最多交换c[i]次),问最终n个人的队列次序。

思路

我们发现第(i)个人的位置只能在(i-c_i,i-c_i+1,...i)这些位置。如果(i>c_i+1)的话,(1,2,...i-c_i+1)这些位置是不能通过交换到达的。
所以,我们可以建一棵区间(splay),节点间的关系不是依靠值而是依靠位置,即每个节点构成的子树代表了这些人构成的一段区间。同时在每个节点上记录原先位置和区间最大值,方便我们接下来的操作。
每次操作时,若(i>c_i+1),把前(i-c_i-2)个数拉到根的左子树里,第(i-c_i-1)个数作为根,在根的右子树里插入第(i)个人。插入时,判断当前这个人的(a_i)是否大于右子树的最大值以及当前递归到的节点,如果是就往左子树递归(说明这个人可以和右子树对应区间的人全部交换而到达左子树),否则就往右子树递归。
如果(i<=c_i+1),则表明当前这个人可以与(splay)内任何一个人交换,直接从根开始递归。
每次操作完之后将新插入的节点(splay)到根,保证(splay)的复杂度。

#include<bits/stdc++.h>
using namespace std;
const int maxx = 1e5+10;
int ch[maxx][2],fa[maxx],siz[maxx],ma[maxx],val[maxx];
//ma维护子树即区间的最大值,val保存节点在序列原先的位置
int rt,sz;
int a[maxx],c[maxx];
int get(int x)
{
    return ch[fa[x]][1]==x;
}
void update(int x)
{
    siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
    ma[x]=max(a[val[x]],max(ma[ch[x][0]],ma[ch[x][1]]));
}
void rotate(int x)
{
    int y=fa[x],z=fa[y],k=get(x);
    ch[y][k]=ch[x][k^1];fa[ch[y][k]]=y;
    ch[x][k^1]=y;fa[y]=x;fa[x]=z;
    if(z)ch[z][ch[z][1]==y]=x;
    update(y);update(x);
}
void splay(int x,int goal)
{
    for(int y;(y=fa[x])!=goal;rotate(x))
        if(fa[y]!=goal)rotate((get(x)==get(y))?y:x);
    if(goal==0)rt=x;
}
int findkth(int k)
{
    int x=rt;
    while(1)
    {
        if(k<=siz[ch[x][0]])x=ch[x][0];
        else
        {
            k-=siz[ch[x][0]]+1;
            if(!k)return x;
            x=ch[x][1];
        }
    }
}
void Insert(int &x,int f,int id)
{
    if(!x)
    {
        x=++sz;
        fa[x]=f;siz[x]=1;
        ma[x]=a[id];val[x]=id;
        return;
    }
    if(a[id]>ma[ch[x][1]]&&a[id]>a[val[x]])Insert(ch[x][0],x,id);
    else Insert(ch[x][1],x,id);
    update(x);
}
void dfs(int x)
{
    if(ch[x][0])dfs(ch[x][0]);
    printf("%d ",val[x]);
    if(ch[x][1])dfs(ch[x][1]);
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&a[i],&c[i]);
    for(int i=1;i<=n;i++)
    {
        if(i-c[i]<=1)Insert(rt,0,i);
        else
        {
            int k=findkth(i-c[i]-1);
            splay(k,0);
            Insert(ch[rt][1],rt,i);
        }
        splay(sz,0);
    }
    dfs(rt);
    return 0;
}
原文地址:https://www.cnblogs.com/HooYing/p/12494748.html