吉如一线段树

本文深入探讨了段式树状数组的高级应用,包括区间加、取min、求区间和、最大值及历史最大值等操作。通过具体代码实现,详细讲解了如何在段式树中维护多个复杂属性,如区间最值、次大值、历史最值和懒惰标记。适用于解决复杂的数据结构和算法问题。
部署运行你感兴趣的模型镜像

例题

深深意识到我是一只菜鸡

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 550010
#define ll long long
#define INF 0x3f3f3f3f
struct tree{
    int l,r;
    int mx;//区间最值
    int mx_;//区间历史最值
    int se;//次大
    int cnt;
    ll sum;//区间和
    int add1,add1_,add2,add2_;//四个懒惰标记
    //add1_维护mx_. add2和add2_维护se
}t[N<<2];
int n,m,f;
int a[N];

void pushup(int p){//更新区间和,区间最大值,次大值,历史最大值
    t[p].sum=t[p*2].sum+t[p*2+1].sum;
    t[p].mx_=max(t[p*2].mx_,t[p*2+1].mx_);
    if(t[p*2].mx==t[p*2+1].mx){
        t[p].mx=t[p*2].mx;
        t[p].se=max(t[p*2].se,t[p*2+1].se);
        t[p].cnt=t[p*2].cnt+t[p*2+1].cnt;
    }
    else if(t[p*2].mx>t[p*2+1].mx){
        t[p].mx=t[p*2].mx;
        t[p].se=max(t[p*2].se,t[p*2+1].mx);
        t[p].cnt=t[p*2].cnt;
    }
    else{
        t[p].mx=t[p*2+1].mx;
        t[p].se=max(t[p*2].mx,t[p*2+1].se);
        t[p].cnt=t[p*2+1].cnt;
    }
}



void build(int p,int l,int r){
    t[p].l=l,t[p].r=r;
    t[p].add1=t[p].add1_=t[p].add2=t[p].add2_=0;
    if(l==r){
        t[p].sum=t[p].mx_=t[p].mx=a[l];
        t[p].se=-INF,t[p].cnt=1;
        return ;
    }
    int mid =l+r>>1;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    pushup(p);
}


void update(int p,int k1,int k1_,int k2,int k2_){
    //标记下传时更新当前p的值
    //k1为区间增加标记
    //k1_为加减标记的历史最大值
    //k2为非最大值加减标记
    //k2_为非最大值历史加减标记
    t[p].sum+=1ll*k1*t[p].cnt+1ll*k2*(t[p].r-t[p].l+1-t[p].cnt);
    t[p].mx_=max(t[p].mx_,t[p].mx+k1_);//更新区间历史最大值
    t[p].add1_=max(t[p].add1_,t[p].add1+k1_);//更新标记
    t[p].mx+=k1,t[p].add1+=k1;//更新区间最大值和懒惰标记
    t[p].add2_=max(t[p].add2_,t[p].add2+k2_);//更新非最大值标记
    if(t[p].se!=-INF) t[p].se+=k2;
    t[p].add2+=k2;
}

void pushdown(int p){//标记下穿
    int temp=max(t[p*2].mx,t[p*2+1].mx);
    if(t[p*2].mx==temp)//如果左子树有最大值的话 就需要更新左子树
        update(2*p,t[p].add1,t[p].add1_,t[p].add2,t[p].add2_);
    else update(2*p,t[p].add2,t[p].add2_,t[p].add2,t[p].add2_);
    if(t[p*2+1].mx==temp)//如果右子树有最大值的话 就需要更新左子树
        update(2*p+1,t[p].add1,t[p].add1_,t[p].add2,t[p].add2_);
    else update(2*p+1,t[p].add2,t[p].add2_,t[p].add2,t[p].add2_);
    //即使没有最大值 也需要更新其他标记
    t[p].add1=t[p].add1_=t[p].add2=t[p].add2_=0;
}


void modify1(int p,int l,int r,int k){//区间加
    if(t[p].l>r||t[p].r<l) return ;
    if(l<=t[p].l&&r>=t[p].r){
        update(p,k,k,k,k);return ;//全部加上k
    }
    pushdown(p);
    modify1(2*p,l,r,k),modify1(2*p+1,l,r,k);
    pushup(p);
}


void modify2(int p,int l,int r,int k){//取min(a[i],v);
    if(t[p].l>r||t[p].r<l||k>=t[p].mx) return ;
    if(l<=t[p].l&&r>=t[p].r&&k>t[p].se){//更新最大值
        update(p,k-t[p].mx,k-t[p].mx,0,0);return ;
    }
    pushdown(p);
    modify2(p*2,l,r,k),modify2(p*2+1,l,r,k);
    pushup(p);
}

ll query3(int p,int l,int r){//求区间和
    if(t[p].l>r||t[p].r<l) return 0;
    if(l<=t[p].l&&r>=t[p].r) return t[p].sum;
    pushdown(p);
    return query3(p*2,l,r)+query3(p*2+1,l,r);
}

int query4(int p,int l,int r){//求区间最大值
    if(t[p].l>r||t[p].r<l) return -INF;
    if(l<=t[p].l&&r>=t[p].r) return t[p].mx;
    pushdown(p);
    return max(query4(p*2,l,r),query4(p*2+1,l,r));
}

int query5(int p,int l,int r){//求历史最大值
    if(t[p].l>r||t[p].r<l) return -INF;
    if(l<=t[p].l&&r>=t[p].r) return t[p].mx_;
    pushdown(p);
    return max(query5(p*2,l,r),query5(p*2+1,l,r));
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    build(1,1,n);
    while(m--){
        cin>>f;
        int l,r,k;
        if(f==1){
            cin>>l>>r>>k;
            modify1(1,l,r,k);
        }
        else if(f==2){
            cin>>l>>r>>k;
            modify2(1,l,r,k);
        }
        else if(f==3) {
            cin>>l>>r;
            cout << query3(1, l, r) << endl;
        }
        else if(f==4) {
            cin>>l>>r;
            cout << query4(1, l, r) << endl;
        }
        else if(f==5){
            cin>>l>>r;
            cout << query5(1, l, r) << endl;
        }
    }
    return 0;
}

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

L3-027 可怜的复杂度 分数 80 作者 吉如一 单位 北京大学 可怜有一个数组 A,定义它的复杂度 c(A) 等于它本质不同的子区间个数。举例来说,c([1,1,1])=3,因为 [1,1,1] 只有 3 个本质不同的子区间 [1]、[1,1] 和 [1,1,1];而 c([1,2,1])=5,它包含 5 个本质不同的子区间 [1]、[2]、[1,2]、[2,1]、[1,2,1]。 可怜打算出一道和复杂度相关的题目。众所周知,引入随机性往往可以让一个简单的题目脱胎换骨。现在,可怜手上有一个长度为 n 的正整数数组 x 和一个正整数 m。接着,可怜会独立地随机产生 n 个 [1,m] 中的随机整数 y i ​ ,并把 x i ​ 修改为 mx i ​ +y i ​ 。 显然,一共有 N=m n 种可能的结果数组。现在,可怜想让你求出这 N 个数组的复杂度的和。 输入格式: 第一行给出一个整数 t (1≤t≤5) 表示数据组数。 对于每组数据,第一行输入两个整数 n 和 m (1≤n≤100,1≤m≤10 9 ),第二行是 n 个空格隔开的整数表示数组 x 的初始值 (1≤x i ​ ≤10 9 )。 输出格式: 对于每组数据,输出一行一个整数表示答案。答案可能很大,你只需要输出对 998244353 取模后的结果。 输入样例: 4 3 2 1 1 1 3 2 1 2 1 5 2 1 2 1 2 1 10 2 80582987 187267045 80582987 187267045 80582987 187267045 80582987 187267045 80582987 187267045 输出样例: 36 44 404 44616 代码长度限制 16 KB 时间限制 8000 ms 内存限制 256 MB 栈限制 131072 KB C++ (g++) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <bits/stdc++.h> using namespace std; using ll = long long; static const int MOD = 998244353; // 快速幂 a^e mod ll modpow(ll a, ll e){ ll r = 1; a %= MOD; while(e){ if(e & 1) r = r * a % MOD; a = a * a % MOD; e >>= 1; } return r; } // 后缀自动机 struct SAM { struct State { int len, link; map<int,int> next; ll cnt; // endpos 的数量 }; vector<State> st; int last, sz; SAM(int maxn){ st.resize(2*maxn); st[0].len = 0; st[0].link = -1; st[0].cnt = 0; last = 0; sz = 1; } void extend(int c){ int cur = sz++;
最新发布
06-17
### 关于数组复杂度计算及算法实现 在处理与数组复杂度计算相关的算法时,可以采用多种方法进行优化。例如,在查找子串或比较字符串时,后缀自动机(SAM)是一种高效的工具[^1]。它能够以线性时间复杂度构建,并支持快速的模式匹配和子串查询。 对于数组复杂度计算中的二分查找优化,可以通过结合哈希值检查来减少时间复杂度。具体来说,当需要判断某个子串是否小于另一字符串时,可以通过预处理哈希值实现 \(O(1)\) 的比较操作[^2]。这种方法显著提高了效率,尤其是在处理大规模数据时。 以下是一个基于后缀自动机(SAM)的代码示例,用于处理字符串相关问题: ```python class SAM: def __init__(self): self.states = [{}] self.link = [-1] self.length = [0] def extend(self, c): cur = len(self.states) self.states.append({}) self.link.append(-1) self.length.append(self.length[-1] + 1) p = len(self.states) - 2 while p != -1 and c not in self.states[p]: self.states[p][c] = cur p = self.link[p] if p == -1: self.link[cur] = 0 else: q = self.states[p][c] if self.length[p] + 1 == self.length[q]: self.link[cur] = q else: clone = len(self.states) self.states.append(self.states[q].copy()) self.link.append(self.link[q]) self.length.append(self.length[p] + 1) while p != -1 and self.states[p][c] == q: self.states[p][c] = clone p = self.link[p] self.link[q] = self.link[clone] = clone # 示例:构建后缀自动机并查询子串 sam = SAM() s = "ababc" for char in s: sam.extend(char) ``` 上述代码展示了如何通过后缀自动机构建字符串的状态转移图。这种结构非常适合处理动态字符串查询问题,例如子串出现次数统计或最长重复子串查找。 ### 复杂度分析 在实际应用中,后缀自动机的时间复杂度为 \(O(n)\),其中 \(n\) 是字符串长度[^1]。这使得它成为一种高效的解决方案,尤其适用于需要频繁查询的场景。此外,结合二分查找和哈希值验证,可以在 \(O(\log n)\) 时间内完成特定范围的子串比较。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值