NOIP 模拟 序列操作 - 无旋treap

题意:

一开始有n个非负整数h[i],接下来会进行m次操作,第i次会给出一个数c[i],要求选出c[i]个大于0的数并将它们-1,问最多可以进行多少次?

分析:

首先一个显然的贪心就是每次都将最大的c[i]个数-1,于是就可以用无旋式treap来维护,基本操作中split_k和split_v都使用普通的merge,但在提取区间并打完标记后,因为整个序列的单调性发生改变,需要使用启发式合并(只在修改过后使用)。

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
#include<ctime>
#include<queue>
using namespace std;
template<typename T>
inline void read(T &x) {
    T i = 0, f = 1;
    char ch = getchar();
    for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
    if(ch == '-') f = -1, ch = getchar();
    for(; ch >= '0' && ch <= '9'; ch = getchar()) i = (i << 3) + (i << 1) + (ch - '0');
    x = i * f;
}
template<typename T>
inline void wr(T x) {
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) wr(x / 10);
    putchar(x % 10 + '0');
}
const int N = 1e6 + 50, OO = 0x3f3f3f3f;
int n, m, h[N], c[N];
inline int Rand(){
    static int RAND_VAL = 1388593021;
    return RAND_VAL += RAND_VAL << 2 | 1;
}
#define SZ(x) (x?x->sze:0)
#define V(x) (x?x->val:OO)
struct node{
    node *lc, *rc;
    int sze, pri, val, tag;
    node():lc(NULL), rc(NULL){}
    inline void add(int v){
        val += v, tag += v;
    }
    inline void pushDown(){
        if(tag != 0){
            if(lc) lc->add(tag);
            if(rc) rc->add(tag);
            tag = 0;
        }
    }
    inline node* upt(){
        sze = SZ(lc) + SZ(rc) + 1;
        return this;
    }
    inline void print(){
        pushDown();
        if(lc) lc->print();
        cout<< val<<" ";
        if(rc) rc->print();
    }
}pool[N], *tail = pool, *rt = NULL;
inline node* newNode(int v){
    node *x = tail++;
    x->val = v;
    x->lc = x->rc = NULL;
    x->pri = Rand();
    x->tag = 0;
    x->sze = 1;
    return x;
}
inline node* Merge_o(node *x, node *y){
    if(!x) return y;
    if(!y) return x;
    node *L, *R;
    if(x->pri < y->pri){
        x->pushDown();
        x->rc = Merge_o(x->rc, y);
        return x->upt();
    }
    else {
        y->pushDown();
        y->lc = Merge_o(x, y->lc);
        return y->upt();
    }
}
inline void Split_v(node *x, int v, node *&L, node *&R);
inline node* Merge(node *x, node *y){
    if(!x) return y;
    if(!y) return x;
    x->pushDown(), y->pushDown();
    if(x->pri > y->pri) swap(x, y);
    node *L, *R;
    Split_v(y, x->val, L, R);
    x->lc = Merge(x->lc, L);
    x->rc = Merge(x->rc, R);
    x->upt();
    return x;
}
inline void Split_k(node *x, int k, node *&L, node *&R){
    if(x == NULL){L = NULL, R = NULL; return;}
    x->pushDown();
    if(SZ(x->lc) < k){
        Split_k(x->rc, k - SZ(x->lc) - 1, L, R);
        x->rc = NULL; x->upt();
        L = Merge_o(x, L);
    }
    else{
        Split_k(x->lc, k, L, R);
        x->lc = NULL; x->upt();
        R = Merge_o(R, x);
    }
}
inline void Split_v(node *x, int v, node *&L, node *&R){
    if(x == NULL){L = NULL, R = NULL; return;}
    x->pushDown();
    if(V(x) <= v){
        Split_v(x->rc, v, L, R);
        x->rc = NULL; x->upt();
        L = Merge_o(x, L);
    }
    else{
        Split_v(x->lc, v, L, R);
        x->lc = NULL, x->upt();
        R = Merge_o(R, x);
    }
}
inline node* build(){
    static node* stk[N];
    node* pre;
    int top = 0;
    for(register int i = 1; i <= n; i++){
        node *x = newNode(h[i]);
        pre = NULL;
        while(top && stk[top]->pri > x->pri)
            pre = stk[top]->upt(),  stk[top--] = NULL;
        if(stk[top]) stk[top]->rc = x;
        x->lc = pre; stk[++top] = x;
    }
    while(top) stk[top--]->upt();
    return stk[1];
}
int main(){
    freopen("h.in", "r", stdin);
    read(n), read(m);
    for(register int i = 1; i <= n; i++) read(h[i]);
    for(register int i = 1; i <= m; i++) read(c[i]);
    sort(h + 1, h + n + 1);
    rt = build();
    for(register int i = 1; i <= m; i++){
        if(!c[i]) continue;
        if(c[i] > n){
            wr(i - 1);
            return 0;
        }
        node *L, *R, *p, *q;
        Split_v(rt, 0, L, R);
        if(SZ(R) >= c[i]){
            Split_k(R, SZ(R) - c[i], p, q);
            if(q) q->add(-1);
            R = Merge(p, q);
        }
        else{
            wr(i - 1);
            return 0;
        }
        rt = Merge(L, R);
    }
    wr(m);
    return 0;
}

转载于:https://www.cnblogs.com/CzYoL/p/7794407.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值