关于此题[ABC343F] Second Largest Query 线段树子区间合并类问题的一些总结

传送门

  • 题目大意:给定序列,每次操作可以单点修改以及询问每个区间内严格次大值出现次数。
  • 此类区间合并的线段树之前也做过,但是都没有一个固定的写法,导致调了很久都过不了,感觉上是写丑了。对于一个节点要维护多个信息,我们可以用结构体来实现,并且pushup操作,即左右儿子两个区间合并操作,可以直接返回node类型到当前节点,这样的好处是询问操作也直接用node作为返回值可以一个query就得到答案,不然还得一个query找次大值,再来个query找次大值出现次数。
  • 这里的合并操作可以分类讨论,也可以用set完成,我这里用的是set,写起来更加方便且不容易出错。
#include<bits/stdc++.h>
    
using namespace std;
    
long long t;
const long long N = 2e5 + 10;
long long n,m;
long long a[N];
struct node {
    long long max1,max2,num1,num2;
}tr[4*N];

node up(node ls,node rs) {
    node res;
    res.max1 = res.max2 = res.num1 = res.num2 = 0;
    set<long long,greater<long long> > tq;
    tq.insert(ls.max1);
    tq.insert(ls.max2);
    tq.insert(rs.max1);
    tq.insert(rs.max2);
    set<long long>::iterator it = tq.begin();
    res.max1 = *it;
    if(ls.max1 == *it) res.num1 += ls.num1;
    if(ls.max2 == *it) res.num1 += ls.num2;
    if(rs.max1 == *it) res.num1 += rs.num1;
    if(rs.max2 == *it) res.num1 += rs.num2;
    it++;
    if(*it != 0) {
        res.max2 = *it;
        if(ls.max1 == *it) res.num2 += ls.num1;
        if(ls.max2 == *it) res.num2 += ls.num2;
        if(rs.max1 == *it) res.num2 += rs.num1;
        if(rs.max2 == *it) res.num2 += rs.num2;
    }
    return res;
}

void build(long long k,long long l,long long r) {
    if(l == r) {
        tr[k].max1 = a[l];
        tr[k].num1 = 1;
        return;
    }
    long long mid = (l + r) >> 1;
    build(k * 2,l,mid);
    build(k * 2 + 1,mid + 1,r);
    tr[k] = up(tr[k * 2],tr[k * 2 + 1]);
}

void modify(long long k,long long l,long long r,long long pos,long long v) {
    if(l > pos || r < pos) return;
    if(l == r && l == pos) {
        tr[k].max1 = a[l] = v;
        tr[k].max2 = 0;
        tr[k].num1 = 1;
        tr[k].num2 = 0;
        return ;
    }
    long long mid = (l + r) >> 1;
    modify(k * 2,l,mid,pos,v);
    modify(k * 2 + 1,mid + 1,r,pos,v);
    tr[k] = up(tr[k * 2],tr[k * 2 + 1]);
}

node query(long long k,long long l,long long r,long long x,long long y) {
    if(l > y || r < x) return {0,0,0,0};
    if(l >= x && r <= y) return tr[k];
    long long mid = (l + r) >> 1;
    if(y <= mid) return query(k * 2,l,mid,x,y);
    else if(x > mid) return query(k * 2 + 1,mid + 1,r,x,y);
    else return up(query(k * 2,l,mid,x,y),query(k * 2 + 1,mid + 1,r,x,y));
}
    
void solve() {
    cin >> n >> m;
    for(long long i = 1;i <= n;i++) cin >> a[i];
    build(1,1,n);
    for(long long i = 1;i <= m;i++) {
        long long pd,x,y;
        cin >> pd >> x >> y;
        if(pd == 1)
            modify(1,1,n,x,y);
        else cout << query(1,1,n,x,y).num2 << '\n';
    }
}
    
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    t = 1;
    while(t--) solve();
    
    return 0;
}
posted @   孤枕  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
山不在高,有仙则名。水不在深,有龙则灵。
点击右上角即可分享
微信分享提示