POJ-1442

模板题:treap模板可直接用 改变一下主函数输入就可以 具体看代码注释

#include<bits/stdc++.h>
using namespace std;
const int N = 5000001;
int id[N];
struct Node {
    int size;//以当前节点为根的子树的大小
    int rank;//用以维护treap的随机数
    int key;//键值 这里是武功级别
    Node* son[2];
    // 小于比较 自身rank和传入的rank
    bool operator < (const Node& a) const {
        return rank < a.rank;
    }
    // 比较x和自身key
    // 相等返回-1
    // x<key 左子树 0
    // x>key 右子树 1
    int cmp(int x) const {
        if (key == x) return -1;
        return x < key ? 0 : 1;
    }
    // 更新子树的大小 旋转时treap存在节点需要更新子树大小
    void update() {
        size = 1;
        if (son[0]) size += son[0]->size;
        if (son[1]) size += son[1]->size;
    }
};

// 旋转 d=0左旋 d=1右旋
void rotate(Node*& o, int d) {
    Node* k = o->son[1 ^ d];
    o->son[1 ^ d] = k->son[d];
    k->son[d] = o;
    o->update();
    k->update();
    o = k;//这里把原来指向o的位置改了 所以原来指向o的指针现在相当于指向k 也是传指针的引用的原因
}

void insert(Node*& o, int x) {
    if (!o) {
        o = new Node();//直接在原位置创建指针 这是穿指针的引用的原因
        o->son[0] = o->son[1] = nullptr;
        o->rank = rand();
        o->key = x;
        o->size = 1;
    }
    else {
        int d = o->cmp(x);
        insert(o->son[d], x);
        o->update();
        if (o < o->son[d]) {
            rotate(o, 1 ^ d);
        }
    }
}

// 返回第k大的数
int kth(Node* o, int k) {
    if (o == nullptr || k<0 || k>o->size) {
        return -1;
    }
    else {
        int s = (o->son[1] == nullptr) ? 0 : o->son[1]->size;
        if (k == s + 1) return o->key;
        else if (k < s + 1) {
            return kth(o->son[1], k);
        }
        else {
            return kth(o->son[0], k - s - 1);
        }
    }
}

// 找到k的位次
int find(Node* o, int k) {
    if (!o) return -1;
    else {
        int d = o->cmp(k);
        if (d == -1) {
            return o->son[1] == nullptr ? 1 : o->son[1]->size + 1;
        }
        else if (d == 1) {
            return find(o->son[1], k);
        }
        else {
            int tmp = find(o->son[0], k);
            if (tmp == -1) return -1;
            return o->son[1] == nullptr ? tmp + 1 : tmp + o->son[1]->size + 1;
        }
    }
}

void deleteNode(Node* root) {
    if (!root) return;
    deleteNode(root->son[0]);
    deleteNode(root->son[1]);
    delete root;
}
/*
本题的大概意思是 给定序列
指明某一时刻 序列第几大的元素
和排序有关选用treap实现名次树
关键在于读取的方式
*/
int n, m;
int val[1000010];
int main()
{
    while (cin >> n >> m) {
        Node* root = NULL;
        int id = 1;//代表当前想要查询第几大
        //这是一个很好的思想由于本题需要延迟插树 先缓存到数组中
        for (int i = 1; i <= n; i++)
            cin >> val[i];
        //关键在于如何获得对应时刻的搜索树
        int pre = 1;
        for (int i = 1; i <= m; i++) {
            int t;
            cin >> t;
            //表示继续接着插入 从上一次的id到本次需要读取的位置 表示t时刻
            for (int j = pre; j <= t; j++) {
                insert(root, val[j]);
            }
            cout<<kth(root, root->size-id+1)<<endl;
            id++;
            pre = t+1;//保存上一时刻插入的位置
        }
        deleteNode(root);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值