洛谷P4314 CPU【线段树】

这篇博客介绍了如何使用区间数据结构和懒惰 Propagation 技术解决 CPU 使用率监控问题。文中详细阐述了如何处理区间加法、设置、查询最大值和历史最大值的操作,通过维护区间最大值和历史最大值以及相应的懒惰标记,实现了高效的数据更新和查询。代码示例展示了如何实现这些操作,包括区间加法、设置和查询的下传过程。
摘要由CSDN通过智能技术生成

题意

Bob 需要一个程序来监视 CPU 使用率。这是一个很繁琐的过程,为了让问题更加简单,Bob 会慢慢列出今天会在用计算机时做什么事。

Bob 会干很多事,除了跑暴力程序看视频之外,还会做出去玩玩和用鼠标乱点之类的事,甚至会一脚踢掉电源……这些事有的会让做这件事的这段时间内 CPU 使用率增加或减少一个值;有的事还会直接让 CPU 使用率变为一个值。

当然 Bob 会询问:在之前给出的事件影响下,CPU 在某段时间内,使用率最高是多少。有时候 Bob 还会好奇地询问,在某段时间内 CPU 曾经的最高使用率是多少。

为了使计算精确,使用率不用百分比而用一个整数表示。

不保证 Bob 的事件列表出了莫名的问题,使得使用率为负………………

思路

需要支持的区间操作有:

  • 区间加法
  • 区间设置
  • 区间查询最大值
  • 区间查询历史最大值

对于查询最大值的操作,是比较经典的操作,维护一个区间最大值 m x mx mx,并用两个懒标记 a d d l z addlz addlz s e t l z setlz setlz辅助。
对于查询历史最大值的操作,也是维护一个区间历史最大值 m x _ h i s mx\_his mx_his,并用两个懒标记 h i s _ a d d his\_add his_add h i s _ s e t his\_set his_set辅助。
区间合并操作十分简单,两个子区间取一下最大值即可。下传懒标记比较麻烦,下面进行分析。
首先,懒标记的本质其实是,当前区间的若干操作序列的合并(加法标记是若干次加法操作的合并,设置标记是若干次设置操作的合并),可以发现,如果某个区间出现一次设置操作,之后的所有加法操作就可以等效地看成设置操作
所以,每个区间的操作序列只有两种情况:

  • 全都是加法操作
  • 先加法操作再设置操作(若没有加法操作也不影响)

首先考虑加法标记的下传,若子区间没有设置操作(设置标记为空),那么进行加法标记的下传;否则把加法标记转化为设置标记的下传。加法标记下传实现如下:

void push_Add(int i, int add, int his_add) // i: 子区间对应的节点 add: 父亲节点的加法标记 his_add: 父亲节点的历史最大加法标记累加
{
	// 历史最大值 有两种可能,一种是原序列的历史最大值,一种是原序列最大值加上新的操作序列之后的最大值
    t[i].mx_his = max(t[i].mx + his_add, t[i].mx_his);
    // 与历史最大值类似     
    t[i].add_his = max(t[i].addlz + his_add, t[i].add_his);
    // 最大值和加法标记直接更新即可
    t[i].mx += add;
    t[i].addlz += add; 
}

对于设置标记的下传,若父区间存在设置标记,则直接下传即可。实现如下:

void push_Set(int i, int set, int his_set) // i: 子区间对应的节点 set: 父亲节点的设置标记 his_set: 父亲节点的历史最大设置标记
{
    if (t[i].has_set) // 如果子节点存在设置标记,要与父节点的设置标记比较一下,其他的下传都比较简单
    {
        t[i].mx = set;
        t[i].setlz = set;
        t[i].mx_his = max(his_set, t[i].mx_his);
        t[i].set_his = max(t[i].set_his, his_set); 
    }
    else 
    {
        t[i].has_set = true;
        t[i].mx = set;
        t[i].setlz = set;
        t[i].mx_his = max(his_set, t[i].mx_his);
        t[i].set_his = his_set;
    }
}

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <cmath>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int N = 100005;

int a[N];

struct TreeNode
{
    int l, r;
    int mx, mx_his;
    int addlz, setlz;
    int add_his, set_his;
    bool has_set;
}t[N << 2];

void push_up(int i)
{
    t[i].mx = max(t[i << 1].mx, t[i << 1 | 1].mx);
    t[i].mx_his = max(t[i << 1].mx_his, t[i << 1 | 1].mx_his);
}

void push_Add(int i, int add, int his_add)
{
    t[i].mx_his = max(t[i].mx + his_add, t[i].mx_his);
    t[i].add_his = max(t[i].addlz + his_add, t[i].add_his);
    t[i].mx += add;
    t[i].addlz += add; 
}

void push_Set(int i, int set, int his_set)
{
    if (t[i].has_set)
    {
        t[i].mx = set;
        t[i].setlz = set;
        t[i].mx_his = max(his_set, t[i].mx_his);
        t[i].set_his = max(t[i].set_his, his_set);
    }
    else 
    {
        t[i].has_set = true;
        t[i].mx = set;
        t[i].setlz = set;
        t[i].mx_his = max(his_set, t[i].mx_his);
        t[i].set_his = his_set;
    }
}

void push_down(int i)
{
    if (t[i << 1].has_set)
        push_Set(i << 1, t[i << 1].setlz + t[i].addlz, t[i << 1].setlz + t[i].add_his);
    else 
        push_Add(i << 1, t[i].addlz, t[i].add_his);
    if (t[i << 1 | 1].has_set)
        push_Set(i << 1 | 1, t[i << 1 | 1].setlz + t[i].addlz, t[i << 1 | 1].setlz + t[i].add_his);
    else 
        push_Add(i << 1 | 1, t[i].addlz, t[i].add_his);
    if (t[i].has_set)
    {
        push_Set(i << 1, t[i].setlz, t[i].set_his);
        push_Set(i << 1 | 1, t[i].setlz, t[i].set_his);
    }

    t[i].has_set = false;
    t[i].addlz = t[i].add_his = t[i].setlz = t[i].set_his = 0;
}

void build(int i, int l, int r)
{
    t[i].l = l, t[i].r = r;
    if (l == r)
    {
        t[i].mx = t[i].mx_his = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(i << 1, l, mid);
    build(i << 1 | 1, mid + 1, r);
    push_up(i);
}

void updateAdd(int i, int l, int r, int k)
{
    if (l <= t[i].l && t[i].r <= r) 
    {
        if (t[i].has_set)
            push_Set(i, t[i].setlz + k, t[i].setlz + k);
        else 
            push_Add(i, k, k);
        return;
    }
    push_down(i);
    int mid = (t[i].l + t[i].r) >> 1;
    if (l <= mid) updateAdd(i << 1, l, r, k);
    if (r > mid) updateAdd(i << 1 | 1, l, r, k);
    push_up(i);
}

void updateSet(int i, int l, int r, int k)
{
    if (l <= t[i].l && t[i].r <= r)
    {
        push_Set(i, k, k);
        return;
    }
    push_down(i);
    int mid = (t[i].l + t[i].r) >> 1;
    if (l <= mid) updateSet(i << 1, l, r, k);
    if (r > mid) updateSet(i << 1 | 1, l, r, k);
    push_up(i);
}

int queryMx(int i, int l, int r)
{
    if (l <= t[i].l && t[i].r <= r) return t[i].mx;
    push_down(i);
    int s = -0x3f3f3f3f;
    int mid = (t[i].l + t[i].r ) >> 1;
    if (l <= mid) s = max(s, queryMx(i << 1, l, r));
    if (r > mid) s = max(s, queryMx(i << 1 | 1, l, r));
    return s;
}

int queryHisMx(int i, int l, int r)
{
    if (l <= t[i].l && t[i].r <= r) return t[i].mx_his;
    push_down(i);
    int s = -0x3f3f3f3f;
    int mid = (t[i].l + t[i].r) >> 1;
    if (l <= mid) s = max(s, queryHisMx(i << 1, l, r));
    if (r > mid) s = max(s, queryHisMx(i << 1 | 1, l, r));
    return s;
}

int main()
{
    #ifdef ZYCMH
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    #endif
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ )
        scanf("%d", &a[i]);

    build(1, 1, n);

    int m;
    scanf("%d", &m);
    for (int i = 1; i <= m; i ++ )
    {
        char op;
        int l, r, x;
        scanf("\n%c", &op);
        scanf("%d%d", &l, &r);
        if (op == 'A') printf("%d\n", queryHisMx(1, l, r));
        else if(op == 'Q') printf("%d\n", queryMx(1, l, r));
        else if(op == 'P')
        {
            scanf("%d", &x);
            updateAdd(1, l, r, x);
        }
        else if(op == 'C')
        {
            scanf("%d", &x);
            updateSet(1, l, r, x);
        }
    }
    return 0;
}

洛谷P1168题目是关于中位数线段树解法的问题。中位数线段树解法可以通过维护两个堆来实现。一个是大根堆,一个是小根堆。每次插入元素时,根据一定的规则来维护这两个堆,使得大根堆的个数在一定情况下比小根堆多1或者相等。大根堆的最后一个元素即为中位数。具体的规则如下: 1. 如果大根堆和小根堆的个数相等,下一次插入的元素一定插入到大根堆。此时判断小根堆的堆顶是否大于当前元素x,如果是,则将小根堆的堆顶元素插入到大根堆,然后将x压入小根堆;否则直接将x压入大根堆。 2. 如果大根堆和小根堆的个数不相等,按照类似的规则进行操作。 通过以上规则,可以实现在每次插入元素时,维护两个堆的平衡,并且保证大根堆的最后一个元素即为中位数。 这种解法的时间复杂度为O(logN),其中N为序列的长度。 #### 引用[.reference_title] - *1* *2* [中位数(洛谷p1168)(堆/树状数组+二分/线段树+二分)](https://blog.csdn.net/qq_45604735/article/details/114382762)[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^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [洛谷 P1168 中位数(权值线段树,离散化)](https://blog.csdn.net/qq_38232157/article/details/127594230)[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^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值