P3165 [CQOI2014]排序机械臂

不难,但是挺坑的。。。

这道题要你解决的问题比较显然能总结出来:每次找出第i大的元素所在位置p,然后对\([i,p]\)区间翻转,每次询问这个第i大的元素所在位置。

看到翻转操作就想到了平衡树,所以用splay解决。

我们可以预处理出原序列中第k大的元素是在第几个。离散化排序一下就是了。

我们从1到n+2这一段数字建一个splay,然后用离散化后数组就可以遍历每一次那个第k大的下标。

为什么要从1到n+2?下面要用到split操作。

如何查找排名?直接把这个东西splay到根,输出左子树size和它自己的。因为引入了虚拟结点所以自己的那个1都不用加。

然后就直接split出这段区间然后搞一个标记即可。

然后发现pushdown这个东西有一个难以实现的地方:我每次print之后答案是对的,但是没有整棵树遍历的话答案是错的。

那么就是你pushdown少了,除了在查找第k大的时候pushdown,在splay的时候也可以pushdown。具体看代码。

注意pushdown的顺序也是从上到下的。

有一个坑点:需要注意的是,如果有高度相同的物品,必须保证排序后它们的相对位置关系与初始时相同。

所以你离散化的时候需要注意,否则爆零。。。

代码:

#include<cstdio>
#include<algorithm>
const int maxn = 100005;

struct Nodes
{
    int val, idx;
} a[maxn];
int size[maxn], fa[maxn], ch[maxn][2], val[maxn];
int rev[maxn];
int root;
int n;
bool cmp(const Nodes x, const Nodes y)
{
    if(x.val == y.val) return x.idx < y.idx;
    return x.val < y.val;
}
int read()
{
    int ans = 0, s = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0'){ if(ch == '-') s = -1; ch = getchar(); }
    while(ch >= '0' && ch <= '9') ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
    return s * ans;
}
int dir(int x)
{
    return ch[fa[x]][1] == x;
}
void connect(int son, int f, int k)
{
    fa[son] = f;
    ch[f][k] = son;
}
void pushup(int x)
{
    size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
}
void pushdown(int x)
{
    if(rev[x])
    {
        if(ch[x][0]) rev[ch[x][0]] ^= 1;
        if(ch[x][1]) rev[ch[x][1]] ^= 1;
        std::swap(ch[x][0], ch[x][1]);
        rev[x] = 0;
    }
}
void rotate(int x)
{
    int y = fa[x];
    int z = fa[y];
    int yk = dir(x);
    int zk = dir(y);
    int b = ch[x][yk ^ 1];
    connect(b, y, yk);
    connect(y, x, yk ^ 1);
    connect(x, z, zk);
    pushup(y);
    pushup(x);
}
void splay(int x, int goal)
{
    while(fa[x] != goal)
    {
        int y = fa[x];
        int z = fa[y];
        pushdown(z);
        pushdown(y);
        pushdown(x);
        if(z != goal) dir(x) == dir(y) ? rotate(y) : rotate(x);
        rotate(x);
    }
    if(goal == 0) root = x;
}
int kth(int k)
{
    int now = root;
    while(2333)
    {
        pushdown(now);
        if(size[ch[now][0]] + 1 < k)
        {
            k -= size[ch[now][0]] + 1;
            now = ch[now][1];
        }
        else if(size[ch[now][0]] >= k) now = ch[now][0];
        else return now;
    }
}
int build(int f, int l, int r)
{
    if(l > r) return 0;
    if(l == r)
    {
        fa[l] = f;
        size[l] = 1;
        val[l] = l;
        return l;
    }
    int mid = (l + r) >> 1;
    fa[mid] = f;
    val[mid] = mid;
    ch[mid][0] = build(mid, l, mid - 1);
    ch[mid][1] = build(mid, mid + 1, r);
    pushup(mid);
    return mid;
}
void print(int u)
{
    pushdown(u);
    if(ch[u][0]) print(ch[u][0]);
    printf("%d ", val[u]);
    if(ch[u][1]) print(ch[u][1]);
}
int main()
{
    n = read();
    for(int i = 2; i <= n + 1; i++) a[i].val = read(), a[i].idx = i;
    root = build(0, 1, n + 2);
    std::sort(a + 2, a + n + 2, cmp);
    for(int i = 2; i <= n + 1; i++)
    {
        splay(a[i].idx, 0);
        int temp = size[ch[root][0]];
        printf("%d ", temp);
        int pre = kth(i - 1), post = kth(temp + 2);
        splay(pre, 0), splay(post, pre);
        int del = ch[post][0];
        rev[del] ^= 1;
        //printf("---- "); print(root); printf("\n");
    }
    return 0;
}
posted @ 2018-09-09 14:38  Garen-Wang  阅读(136)  评论(0编辑  收藏  举报