• 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;
    }
    
  • 相关阅读:
    最小生成树计数
    Tree
    NOJ 成绩排名
    NOJ 成绩排名
    NOJ 成绩排名
    NOJ 成绩排名
    NOJ Physics
    NOJ Physics
    NOJ Physics
    NOJ Physics
  • 原文地址:https://www.cnblogs.com/HooYing/p/12494748.html
Copyright © 2020-2023  润新知