Luogu P2286 [HNOI2004]宠物收养场

平衡树练手题目

刚好学平衡树,这道题直接查询前驱和后继,然后比较差的绝对值即可

不要忘记删除

#include <bits/stdc++.h>
#define pushup(u) if(u -> ls -> siz) u -> siz = u -> ls -> siz + u -> rs -> siz, u -> val = u -> rs -> val
#define new_Node(a, b, c, d) (&(*st[cnt++] = Node(a, b, c, d)))
#define merge(a, b) new_Node(a -> siz + b -> siz, b -> val, a, b)
#define ratio 3
using namespace std;
const int N = 100000 * 2 + 5;
const int MOD = 1000000; 
typedef long long ll;
ll ans = 0;
int cnt = 0, n, opt, x, last;

template <typename T>
inline void read(T &t) {
    t = 0; T m = 1; char ch = getchar(); 
    while(ch < '0' || ch > '9') { if(ch == '-') m = -1; ch = getchar(); }
    while(ch >= '0' && ch <= '9') { t = (t << 3) + (t << 1) + (ch & 15); ch = getchar(); }
    t *= m;
} 

struct Node {
    int val, siz;
    Node *ls, *rs;
    Node () {}
    Node (int a, int b, Node *c, Node *d) : siz(a), val(b), ls(c), rs(d) {}
}*root, *null, *st[N], t[N];

void maintain(Node *u) {
    if(u -> ls -> siz > u -> rs -> siz * ratio) u -> rs = merge(u -> ls -> rs, u -> rs), u -> ls = u -> ls -> ls;
    if(u -> rs -> siz > u -> ls -> siz * ratio) u -> ls = merge(u -> ls, u -> rs -> ls), u -> rs = u -> rs -> rs;
}

void ins(Node *u, int x) {
    if(u -> siz == 1) u -> ls = new_Node(1, min(x, u -> val), null, null), u -> rs = new_Node(1, max(u -> val, x), null, null);
    else ins(x > u -> ls -> val ? u -> rs : u -> ls, x);
    pushup(u), maintain(u);
}

void del(Node *u, int x) {
    if(u -> ls -> siz == 1 && x == u -> ls -> val) *u = *u -> rs;
    else if(u -> rs -> siz == 1 && x == u -> rs -> val) *u = *u -> ls;
    else del(x > u -> ls -> val ? u -> rs : u -> ls, x);
    pushup(u), maintain(u);
}

int kth(Node *u, int x) {
    if(u -> siz == 1) return u -> val;
    return x > u -> ls -> siz ? kth(u -> rs, x - u -> ls -> siz) : kth(u -> ls, x);
}

int rnk(Node *u, int x) {
    if(u -> siz == 1) return 1;
    return x > u -> ls -> val ? rnk(u -> rs, x) + u -> ls -> siz : rnk(u -> ls, x);
}

int main() {
    null = new Node(0, 0, 0, 0);
    root = new Node(1, INT_MAX, null, null);
    for(int i = 0; i < N; i++) st[i] = &t[i];
    read(n);
    for(int i = 1; i <= n; i++) {
        read(opt), read(x);
        if(root -> siz == 1) ins(root, x), last = opt;
        else {
            if(opt == last) ins(root, x);
            else {
                int l1 = kth(root, rnk(root, x) - 1), l2 = kth(root, rnk(root, x + 1));
                if(abs(x - l1) <= abs(x - l2)) {
                    ans = (ans + abs(x - l1)) % MOD;
                    del(root, l1);
                } else {
                    ans = (ans + abs(x - l2)) % MOD;
                    del(root, l2);
                }
            }
        } 
    }
    printf("%lld\n", ans);
    return 0;
}

转载于:https://www.cnblogs.com/chloristendika/p/10066996.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值