hdu 4699 2个栈维护 or 伸展树 (2013多校联合)

hdu 4699  Editor

题意:对一个数列进行操作,光标位置后面插入一个权值为x的数,删除光标前的那个数,光标左移一位,光标右移一位,求1到k位置的最大的前缀和。。

注意这里的k是在光标之前的,由于这个条件,所以这题又简单的2个栈维护可以解,如果没有这个条件,那么就要用伸展树了。

栈的解法叉姐的解题报告有,我这里说说伸展树的做法, 1.8MS卡过。

我们用cur表示光标在第几个数的右边,size表示数的总个数。

对于操作L: 没有移到最左边就cur--

对于操作R: 没有移到最右边就cur++

对于操作D: 把当前的第cur个位置的节点旋到根,再把第cur-1位置的节点旋到根的左边,令根的左右儿子分别为L,R

那么L一定没有右儿子,把L变为根, R变为L的右儿子。

对于操作I x:把当前的第cur个位置的节点旋到根,在根和根的右儿子之间插入一个新节点。

对于操作Q x:相当于询问1------x区间的最大前缀和。把第0个节点旋到根,把第x-1个节点旋到根的右边。

如何求最大前缀和, 维护一个sum[x]表示区间和,ans[x]表示在x为根的区间里的最大前缀和(注意至少要取一个数)。

伸展树:

#include <cstdio>
#include <cstring>
#include <algorithm>
const int maxn = 1000006;
const int inf  = 1e9+6;
using namespace std;
#define L ch[x][0]
#define R ch[x][1]
#define KT ch[ ch[root][1]][0]
int cur, size;
struct splaytree {
    int sz[maxn], ch[maxn][2], pre[maxn];
    int tot, root;
    int sum[maxn], val[maxn], ans[maxn];
    int sta[maxn], top;

    void rotate(int &x, int f) {
        int y = pre[x], z = pre[y];
        ch[y][!f] = ch[x][f];
        pre[ch[x][f]] = y;
        pre[x] = pre[y];
        if(z) ch[z][ch[z][1] == y] = x;
        ch[x][f] = y;
        pre[y] = x;
        up(y);
    }
    void splay(int &x, int g) {
        while(pre[x] != g) {
            int y = pre[x], z = pre[y];
            if(z == g) rotate(x, ch[y][0] == x);
            else {
                int f = (ch[z][0] == y);
                ch[y][!f] == x ? rotate(y, f) : rotate(x, !f);
                rotate(x, f);
            }
        }
        if(!g) root = x;
        up(x);
    }
    void rto(int k, int g) {
        int x = root;
        while(sz[L] != k) {
            if(sz[L] > k) x = L;
            else {
                k -= sz[L]+1;
                x = R;
            }
        }

        splay(x, g);
    }
    void newNode(int &x, int v, int fa) {
        if(top) x = sta[top--];
        else x = ++tot;
        sz[x] = 1;
        pre[x] = fa;
        L = R = 0;
        sum[x] = ans[x] = val[x] = v;
    }
    void init() {
        top = tot = 0;
        cur = size = 0;

        newNode(root, -inf, 0);
        newNode(ch[root][1], -inf, root);
    }
    void insert(int k, int v) {
        rto(k, 0);
//debug();
        int x;
        newNode(x, v, root);
        ch[x][1] = ch[root][1];
        if(ch[x][1])pre[ch[x][1]] = x;
        ch[root][1] = x;
        up(x);
        up(root);
    }
    void erase(int k) {
        rto(k, 0);
        rto(k-1, root);
        sta[++top] = root;
        int l = ch[root][0], r = ch[root][1];
        root = l;
        pre[l] = 0;
        ch[l][1] = r;
        if(r)pre[r] = l;
        up(l);
    }
    void query(int k) {
        rto(0, 0);
        rto(k+1, root);
        printf("%d\n", ans[KT]);
    }
    void up(int x) {
        sz[x] = sz[L] + sz[R] + 1;
        sum[x] = sum[L] + sum[R] + val[x];
        if(!sz[L]) {
            ans[x] = max(val[x], val[x]+ans[R]);
        }
        else {
            ans[x] = max(ans[L], sum[L] + max(val[x], 0));
            ans[x] = max(ans[x], sum[L]+ val[x]+max(0, ans[R]));
        }
    }
    void print(int x) {

        printf("node %d, left %d, right %d, pre %d, sum %d, ans %d, val %d\n", x, L, R, pre[x], sum[x], ans[x], val[x]);
        if(L) print(L);
        if(R) print(R);
    }
    void debug() {
        printf("root = %d cur = %d size = %d\n", root, cur, size);
        print(root);
    }
    void down(int x) {

    }
}spt;
int main() {
    int m, x;
    char op[3];
    while( ~scanf("%d", &m)) {
        spt.init();
        while(m--) {
            scanf("%s", op);
            if(op[0] == 'L') {
                if(cur)cur--;
            }
            else if(op[0] == 'R') {
                if(cur < size)cur++;
            }
            else if(op[0] == 'D') spt.erase(cur--), size--;
            else {
                scanf("%d", &x);
                if(op[0] == 'I') spt.insert(cur++, x), size++;
                else spt.query(x);
            }
        //    spt.debug();
        }
    }
    return 0;
}

栈维护:

#include <cstdio>
#include <cstring>
#include <stack>
using namespace std;
const int maxn = 1000006;
int dp[maxn], sum[maxn], m, x;
const int inf =  1e9+6;
char op[3];
int l[maxn], r[maxn], t1, t2;
int main() {
    while( ~scanf("%d", &m)) {
        dp[0] = -inf;
        t1 = t2 = 0;
        while(m--) {
            scanf("%s", op);
            if(op[0] == 'I') {
                scanf("%d", &x);
                l[++t1] = x;
                sum[t1] = sum[t1-1] + x;
                dp[t1] = max(dp[t1-1], sum[t1]);
            }
            else if(op[0] == 'L') {
                if(!t1) continue;
                r[++t2] = l[t1--];
            }
            else if(op[0] == 'R') {
                if(!t2) continue;
                l[++t1] = r[t2--];
                sum[t1] = sum[t1-1] + l[t1];
                dp[t1] = max(dp[t1-1], sum[t1]);

            }
            else if(op[0] == 'D') t1--;
            else {
                scanf("%d", &x);
                printf("%d\n", dp[x]);
            }
        }

    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值