2021-08-27 势能线段树(吉老师线段树)Ⅰ

吉老师线段树
感觉就是普通的线段树+暴力
常见的套路有区间取模,区间开根等
因为我做题少就见过这俩
你也看见题目上的Ⅰ了,碰到再更新
所以为什么叫吉老师线段树,为什么不叫老头子线段树
于是我去学习了一下历史
在这里插入图片描述
上两道今天写的题:
1.区间开根+区间求和
传送门
因为一个数字开log次就变成1了,于是我们可以暴力修改开根,保存一个区间的maxx,如果这个数为1了那么就不用开了,预计时间复杂度是Nlog的
代码如下:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=200005;
int n,m,a[N];
struct Segtree{
    int l,r,sum,maxx;
}tree[N<<2];
inline void push_up(int root){
    tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
    tree[root].maxx=max(tree[root<<1].maxx,tree[root<<1|1].maxx);
}
void build(int root,int l,int r){
    tree[root].l=l;
    tree[root].r=r;
    if(l==r){
        tree[root].sum=tree[root].maxx=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(root<<1,l,mid);
    build(root<<1|1,mid+1,r);
    push_up(root);
}
void modify(int root,int l,int r,int L,int R){
    if(l==r){
        tree[root].maxx=sqrt(tree[root].maxx);
        tree[root].sum=sqrt(tree[root].sum);
        return ;
    }
    if(tree[root].maxx==1) return ;
    int mid=(l+r)>>1;
    if(L<=mid&&tree[root<<1].maxx>1) modify(root<<1,l,mid,L,R);
    if(R>mid&&tree[root<<1|1].maxx>1) modify(root<<1|1,mid+1,r,L,R);
    push_up(root);
}
int query(int root,int l,int r,int L,int R){
    if(L<=l&&R>=r){
        return tree[root].sum;
    }
    int ans=0;
    int mid=(l+r)>>1;
    if(L<=mid) ans+=query(root<<1,l,mid,L,R);
    if(R>mid)  ans+=query(root<<1|1,mid+1,r,L,R);
    return ans;
}
signed main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    build(1,1,n);
    for(int i=1;i<=m;i++){
        int op,l,r;
        scanf("%lld%lld%lld",&op,&l,&r);
        if(op==1) modify(1,1,n,l,r);
        else printf("%lld\n",query(1,1,n,l,r));
    }
    return 0;
}

2.区间修改+区间开根+区间求和
传送门
线段树神题.jpg
一开始看到这题,直接拿上面那题加了个普通的加法就交上去了,然后顺利成章的就T了(
因为重复操作1和操作2,那么时间复杂度就变成平方了()
所以开根的方式我们得考虑变得更快一些:
必然是对一串数字进行更新,我们考虑一个区间,如果最大值和最小值开根之后减少的值一样,那么这段区间开根不就可以变成区间减法了,再加上上面那个>1的判断,就可以以比nlog稍微多一点的时间复杂度跑过去。
代码如下

#include <bits/stdc++.h>
#define int long long
#define ll long long
using namespace std;
const int N=200005;
int n,m,a[N];
struct NODE{
    int l,r,lazy,sum,maxx,minn;
}tree[N<<2];
void push_up(int root){
    tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
    tree[root].maxx=max(tree[root<<1].maxx,tree[root<<1|1].maxx);
    tree[root].minn=min(tree[root<<1].minn,tree[root<<1|1].minn);
}
void push_down(int root){
    if(tree[root].lazy!=0){
        tree[root<<1].lazy+=tree[root].lazy;
        tree[root<<1|1].lazy+=tree[root].lazy;
        tree[root<<1].maxx+=tree[root].lazy;
        tree[root<<1|1].maxx+=tree[root].lazy;
        tree[root<<1].minn+=tree[root].lazy;
        tree[root<<1|1].minn+=tree[root].lazy;
        tree[root<<1].sum+=(tree[root<<1].r-tree[root<<1].l+1)*tree[root].lazy;
        tree[root<<1|1].sum+=(tree[root<<1|1].r-tree[root<<1|1].l+1)*tree[root].lazy;
        tree[root].lazy=0;
        push_up(root);
    }
    return ;
}
void build(int root,int l,int r){
    tree[root].l=l;
    tree[root].r=r;
    if(l==r){
        tree[root].minn=tree[root].maxx=tree[root].sum=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(root<<1,l,mid);
    build(root<<1|1,mid+1,r);
    push_up(root);
}
void modify(int root,int l,int r,int x){
    tree[root].lazy+=x;
    tree[root].sum+=(r-l+1)*x;
    tree[root].maxx+=x;
    tree[root].minn+=x;
}
void modify2(int root,int l,int r,int L,int R){
    if(L<=l&&R>=r){
        int cha1=tree[root].minn-sqrt(tree[root].minn);
        int cha2=tree[root].maxx-sqrt(tree[root].maxx);
        if(cha1==cha2){//我把区间开根变成区间减法
            modify(root,l,r,-1*cha1);
            return ;
        }
    }
    push_down(root);
    int mid=(l+r)>>1;
    if(L<=mid) modify2(root<<1,l,mid,L,R);
    if(mid<R) modify2(root<<1|1,mid+1,r,L,R);
    push_up(root);
}
void modify2(int root,int l,int r,int L,int R,int x){
    if(L<=l&&R>=r){
        tree[root].lazy+=x;
        tree[root].maxx+=x;
        tree[root].minn+=x;
        tree[root].sum+=(r-l+1)*x;
        return ;
    }
    push_down(root);
    int mid=(l+r)>>1;
    if(L<=mid) modify2(root<<1,l,mid,L,R,x);
    if(mid<R)  modify2(root<<1|1,mid+1,r,L,R,x);
    push_up(root);
}
int query(int root,int l,int r,int L,int R){
    if(L<=l&&R>=r) return tree[root].sum;
    push_down(root);
    int ans=0;
    int mid=(l+r)>>1;
    if(L<=mid) ans+=query(root<<1,l,mid,L,R);
    if(mid<R)  ans+=query(root<<1|1,mid+1,r,L,R);
    return ans;
}
void modify1(int root,int l,int r,int L,int R){
    if(L<=l&&R>=r){
        int cha1=tree[root].maxx-(ll)sqrt(tree[root].maxx);
        int cha2=tree[root].minn-(ll)sqrt(tree[root].minn);
        if(cha1==cha2){
            modify(root,l,r,-cha1);
            return ;
        }
    }
    if(tree[root].maxx==1) return ;
    push_down(root);
    int mid=(l+r)>>1;
    if(L<=mid&&tree[root<<1].maxx>1) modify1(root<<1,l,mid,L,R);
    if(mid<R&&tree[root<<1|1].maxx>1) modify1(root<<1|1,mid+1,r,L,R);
    push_up(root);
}
signed main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    build(1,1,n);
    for(int i=1;i<=m;i++){
        int op,l,r,x;
        scanf("%lld%lld%lld",&op,&l,&r);
        if(op==1)modify1(1,1,n,l,r);
        if(op==2){
            scanf("%lld",&x);
            modify2(1,1,n,l,r,x);
        }
        else if(op==3) printf("%lld\n",query(1,1,n,l,r));
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值