hdu5306 Gorgeous Sequence(线段树)

这篇博客介绍了如何使用线段树解决HDU5306问题,重点在于处理数组更新操作并维护线段树中最大值、次大值和最大值数量。文章通过分析三种更新情况,说明了在特定情况下线段树暴力递归更新的时间复杂度仍然是线性的。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

给你一个数组a长度为n,有三种操作:

  • 0 x y t:a_i=min(a_i,t),x<=i<=y.
  • 1 x y:求max(a_x,a_{x+1}...a_{y-1},a_y).
  • 2 x,y:求\sum_{i=x}^{y}a_i.

刚刚结束的字节跳动camp出了一道取最小值的差不多的题,当时不会,现在来补一下。

  1. 对于一棵线段树我们存一下每个节点管辖区域的最大值mx,次大值se还有最大值的数量cnt。
  2. 每次更新的时候,会有三种情况
    1. t>=mx,管辖区域内全部值不会受本次更新影响;
    2. mx>t>se,管辖区域内只有mx值受到本次更新影响,我们只要把mx更新到t,sum减去mx和t的差值乘上cnt即可;
    3. se>=t,管辖区域内有若干个值受到本次更新影响,那么我们直接暴力递归更新;
    4. 重复以上操作。
  3. 然后操作1和操作2,就是一颗线段树的普通用法了。

接下来,口胡证明一下为什么对于情况三我们暴力更新的时间复杂度是正确的。

  • 我们想一下可以知道,我们更新的复杂度主要来源于对不同的最大值标志的回收与更新。
  • 然后,我们想象一下一棵正常的线段树,易知一棵线段树的最大值标志最多有n种。而且深度随数值减小递增,最多有log层。
  • 一次更新的最大复杂度是把全部标志回收,时间复杂度为nlogn然后可能会以为,这不对呀,这不就吧这个做法证伪了吗。
  • 我们先认为这棵线段树的潜在复杂度是nlogn的,然而我们每次更新会消去若干个最大值标志。并且最多把最大值标志的数两+1,潜在的时间复杂度增加了logn。所以m次操作后的的总时间复杂度还是nlogn级别的。
#include<algorithm>
#include<vector>
#include<iostream>
#include<math.h>
#include<cstring>
#include<string>
#include<stack>
#include<map>
#include<set>
#include<unordered_map>
#include<queue>
#include<assert.h>
#include <iomanip>

#define qcin; ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define pb push_back
#define mp make_pair
#define clr(x) memset(x,0,sizeof x)
#define fmax(x) memset(x,0x3f,sizeof x)
#define finit(x) memset(x,-1,sizeof x)
#define iio(n,m) io(n),io(m)
#define ls(p) (p<<1)
#define rs(p) ((p<<1)|1)

#define dis(l,r) r-l+1
#define gstr(str) scanf("%s",str)
#define glen(str) strlen(str)
using namespace std;

namespace Input{
    const int BUF = 65536;
    char buf[BUF + 1];
    char *head = buf, *tail = buf;
}
inline char inputchar(){
    using namespace Input;
    if(head == tail)
        *(tail = (head = buf) + fread(buf, 1, BUF, stdin)) = 0;
    return *head++;
}
template<class T>
inline void io(T &ret){
    char ch = inputchar();
    while(ch < '0' || ch > '9')
        ch = inputchar();
    ret = ch - '0';
    ch = inputchar();
    while(ch >= '0' && ch <= '9'){
        ret = ret * 10 + ch - '0';
        ch = inputchar();
    }
}

typedef long long ll;

typedef pair<ll,ll>pll;
const int maxn = 5e6+10;
const int mod =  1e8+7;
const ll inf = 1e18;

typedef ll arr[maxn];
typedef char str[maxn];
void file(int x){if(x&&fopen("123.in","r")){freopen("123.in","r",stdin);}}

const long double pi = acos(-1);

int n,m,ql,qr,op,t;
ll val;

struct node{
    ll sum,cnt,mx,se;
}T[maxn];

arr a;

void push_up(int p){
    T[p].sum=T[ls(p)].sum+T[rs(p)].sum;
    if(T[ls(p)].mx>T[rs(p)].mx){
        T[p].mx=T[ls(p)].mx;
        T[p].cnt=T[ls(p)].cnt;
        T[p].se=max(T[ls(p)].se,T[rs(p)].mx);
    }else if(T[ls(p)].mx<T[rs(p)].mx){
        T[p].mx=T[rs(p)].mx;
        T[p].cnt=T[rs(p)].cnt;
        T[p].se=max(T[rs(p)].se,T[ls(p)].mx);
    }else{
        T[p].mx=T[ls(p)].mx;
        T[p].cnt=T[ls(p)].cnt+T[rs(p)].cnt;
        T[p].se=max(T[ls(p)].se,T[rs(p)].se);
    }
}

void push_down(int p){
    int mx=T[p].mx;
    if(mx<T[ls(p)].mx){
        T[ls(p)].sum-=T[ls(p)].cnt*(T[ls(p)].mx-mx);
        T[ls(p)].mx=mx;
    }
    if(mx<T[rs(p)].mx){
        T[rs(p)].sum-=T[rs(p)].cnt*(T[rs(p)].mx-mx);
        T[rs(p)].mx=mx;
    }
}

void build(int p,int l,int r){
    if(l==r){
        T[p].cnt=1;
        T[p].sum=T[p].mx=a[l];
        T[p].se=-1;
        return;
    }
    int md=(l+r)>>1;
    build(ls(p),l,md);
    build(rs(p),md+1,r);
    push_up(p);
}

void update(int p,int l,int r,ll val){
    if(val>=T[p].mx)return;
    if(ql<=l&&qr>=r&&val>T[p].se){
        T[p].sum-=T[p].cnt*(T[p].mx-val);
        T[p].mx=val;
        return;
    }
    int md=(l+r)>>1;
    push_down(p);
    if(md>=ql)update(ls(p),l,md,val);
    if(md<qr)update(rs(p),md+1,r,val);
    push_up(p);
}

ll qry_max(int p,int l,int r){
    if(ql<=l&&qr>=r){
        return T[p].mx;
    }
    ll res=0,md=(l+r)>>1;
    push_down(p);
    if(md>=ql)res=max(res,qry_max(ls(p),l,md));
    if(md<qr)res=max(res,qry_max(rs(p),md+1,r));
    return res;
}

ll qry_sum(int p,int l,int r){
    if(ql<=l&&qr>=r){
        return T[p].sum;
    }
    ll res=0,md=(l+r)>>1;
    push_down(p);
    if(md>=ql)res+=qry_sum(ls(p),l,md);
    if(md<qr)res+=qry_sum(rs(p),md+1,r);
    return res;
}

int main(){
    file(1);
    for(io(t);t--;){
        iio(n,m);
        for(int i=1;i<=n;i++){
            io(a[i]);
        }
        T[0]={0,0,0,-1};
        build(1,1,n);
        while(m--){
            io(op),iio(ql,qr);
            if(!op){
                io(val);
                update(1,1,n,val);
            }else if(op==1){
                printf("%lld\n",qry_max(1,1,n));
            }else if(op==2){
                printf("%lld\n",qry_sum(1,1,n));
            }
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值