codevs 1743 反转卡片

  题意

    有一个长度为n的序列,序列为n的一个排列。每次找到序列的第一个数k,若k=1则退出,否则区间[1,k]进行翻转,直至k=1或翻转次数大于100000。n<=300000

  这题是Splay翻转裸题,但是,我调了一个晚上,毕竟很久没有打过Splay。

  问题是出在一开始构建Splay的过程之中,没有建最大最小的点,而我是用数组写的,第0位设为空,在翻转的过程中,很容易就跑到第0位去了,然后无限TLE,一定要吸取教训啊。

#include <cstdio>
#include <cstdlib>
#include <algorithm>

using namespace std;

#define ls(x) (ch[x][0])
#define rs(x) (ch[x][1])
const int maxn = 300005;
int n, a[maxn];
struct Tree
{
    int ch[maxn][2], siz[maxn], rev[maxn], fa[maxn], val[maxn], cnt, root;
    Tree()
    {
        root = cnt = 0;
    }
    void new_node(int &x, int c)
    {
        x = ++cnt;
        ch[x][0] = ch[x][1] = fa[x] = rev[x] = 0;
        siz[x] = 1;
        val[x] = c;
    }
    void pushup(int x)
    {
        siz[x] = siz[ls(x)]+siz[rs(x)]+1;
    }
    void pushdown(int x)
    {
        if (rev[x] == 0)
            return ;
        swap(ls(x), rs(x));
        rev[ls(x)] ^= rev[x], rev[rs(x)] ^= rev[x];
        rev[x] = 0;
    }
    void build(int &x, int l, int r, int f)
    {
        if (l > r)
            return ;
        int mid = (l+r)>>1;
        new_node(x, a[mid]);
        build(ls(x), l, mid-1, x);
        build(rs(x), mid+1, r, x);
        fa[x] = f;
        pushup(x);
    }
    void rotate(int x, int f)
    {
        int y = fa[x];
        pushdown(x);
        fa[ch[x][!f]] = y;
        ch[y][f] = ch[x][!f];
        fa[x] = fa[y];
        if (fa[y])
            ch[fa[y]][ls(fa[y]) == y ? 0 : 1] = x;
        ch[x][!f] = y;
        fa[y] = x;
        pushup(y);
    }
    void splay(int x, int goal)
    {
        pushdown(x);
        while (fa[x] != goal)
        {
            int y = fa[x], z = fa[y];
            if (z == goal)
            {
                rotate(x, ls(y) == x ? 0 : 1);
                break ;
            }
            int f = ls(z) == y ? 0 : 1;
            if (ch[y][f] == x)
                rotate(y, f), rotate(x, f);
            else
                rotate(x, !f), rotate(x, f);
        }
        pushup(x);
        if (goal == 0)
            root = x;
    }
    void rotateto(int k, int goal)
    {
        int x = root;
        pushdown(x);
        while (siz[ls(x)]+1 != k)
        {
            if (siz[ls(x)] >= k)
                x = ls(x);
            else
            {
                k -= siz[ls(x)]+1;
                x = rs(x);
            }
            pushdown(x);
        }
        splay(x, goal);
    }
    void rever(int k)
    {
        rotateto(1, 0);
        rotateto(k+2, root);
        rev[ls(rs(root))] ^= 1;
    }
    int find(int k)
    {
        int x = root;
        pushdown(x);
        while (siz[ls(x)]+1 != k)
        {
            if (siz[ls(x)] >= k)
                x = ls(x);
            else
            {
                k -= siz[ls(x)]+1;
                x = rs(x);
            }
            pushdown(x);
        }
        return val[x];
    }
}T;

int main()
{
    scanf("%d", &n);
    for (int i = 2; i <= n+1; ++i)
        scanf("%d", &a[i]);
    T.build(T.root, 1, n+2, 0);
    int k = T.find(2), ans = 0;
    while (k != 1)
    {
        T.rever(k);
        ans ++;
        if (ans > 100000)
        {
            ans = -1;
            break ;
        }
        k = T.find(2);
    }
    printf("%d\n", ans);
    return 0;
}

 

转载于:https://www.cnblogs.com/-ZZB-/p/6407811.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值