码迷,mamicode.com
首页 > 其他好文 > 详细

[codevs 1743] 反转卡片

时间:2015-02-07 13:17:35      阅读:254      评论:0      收藏:0      [点我收藏+]

标签:splay   标记传递   

http://codevs.cn/problem/1743/

题解

  • 思路:采用标记的方式减少操作。rev表示该节点及子树需要翻转。如果在kth()查询第k位置的卡片时走到这个节点o,就pushdown(o),把标记传到子节点,反转左右子节点。如果要反转的区间为[l, r],在rever操作中,将l-1伸展到根,再将r+1节点伸展到右节点。那么对应区间可以转化为ch[ch[o][1]][0]节点所对应的树,给它打标记即可。

  • 注意的问题:
    1.需要虚拟节点,因为思路中rever操作很有风险。建立虚拟节点后各个点的编号要变。
    2.在将某节点伸展到根的右节点时,注意k的取值,要减去根左节点的s值+1。见代码。
    3.要注意pushdown操作的位置,共有三处需要:kth中一次,splay中两次。
    3.定义了宏,挺好用的。

代码

总时间耗费: 1782ms
总内存耗费: 5 MB

#include<cstdio>
#include<algorithm>
using namespace std;

const int maxn = 300000 + 10;
const int maxc = 100000;

int n, root, ch[maxn][2], s[maxn], v[maxn];
bool rev[maxn];

#define lc ch[o][0]
#define rc ch[o][1]

void update(int o) { 
    s[o] = s[lc] + s[rc] + 1; 
}

void rotate(int& o, int d) { 
    int k = ch[o][d^1]; ch[o][d^1] = ch[k][d]; 
    ch[k][d] = o; update(o); update(k); o = k;
}

void pushdown(int o) { 
    rev[o]^=1; 
    rev[lc]^=1; 
    rev[rc]^=1; 
    swap(lc, rc); 
}

int cmp(int o, int k) { 
    if(s[lc]+1 == k) return -1; 
    return k < s[lc]+1 ? 0 : 1; 
}

void splay(int& o, int k) {
    if(rev[o]) pushdown(o); //notice
    int d = cmp(o, k);
    if(d == -1) return;
    if(d == 1) k -= s[lc] + 1;
    int p = ch[o][d];
    if(rev[p]) pushdown(p); //notice
    int d2 = cmp(p, k);
    int k2 = (d2 == 0) ? k : k-s[ch[p][0]]-1;
    if(d2 != -1) {
        splay(ch[p][d2], k2);
        if(d == d2) rotate(o, d^1); else rotate(ch[o][d], d);
    }
    rotate(o, d^1);
}

void rever(int& o, int L, int R) { 
    splay(o, L); 
    splay(rc, R-s[lc]+1); //R+2 - (s[lc]+1)
    rev[ch[rc][0]] ^= 1; 
}

void build(int L, int R, int P, int d) {
    if(L == R) { s[L] = 1; ch[P][d] = L; return; }
    int M = (L+R) >> 1;
    if(M-1 >= L) build(L, M-1, M, 0); 
    if(R >= M+1) build(M+1, R, M, 1);
    ch[P][d] = M; update(M);
}

int kth(int o, int k) {
    if(rev[o]) pushdown(o);
    if(s[lc]+1 == k) return o;
    if(s[lc] >= k) return kth(lc, k); 
    return kth(rc, k-s[lc]-1);
}

int main() {
    scanf("%d", &n);
    for(int i = 2; i <= n+1; i++) scanf("%d", &v[i]);
    build(1, n+2, 0, 0); 
    root = (n+3) >> 1;

    int first, c = 0;
    while((first = v[kth(root, 2)]) != 1) {
        rever(root, 1, first);
        if(++c > maxc) { c = -1; break; }
    }
    printf("%d\n", c);
    return 0;
}

[codevs 1743] 反转卡片

标签:splay   标记传递   

原文地址:http://blog.csdn.net/qq_21110267/article/details/43602153

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!