线段树详解

前言

首先,我们要知道线段树是干什么的?

线段树适合各种区间加法运算,如求区间和和求区间最值(不过如果不修改值的话还是用st表吧)。

线段树是一种很好的求区间加法的方法(比树状数组烦,但比树状数组好用)。

查询(logn),修改(logn)。

接下来就让我们来看看线段树如何构建吧。

建树

记住MAXN=4*N,自己画树去。

首先读一个x数组(废话)

然后就可以建树了,如果l==r的话就是没有儿子的节点(叶子),直接赋值为x[l];

然后左子树都在线段[l..mid]上(要不然干啥叫线段树),左儿子编号为root*2;

右子树都在线段[mid..r]上,右儿子编号为root*2+1;

最后更新tree的值(左子树与右子树的区间值,这里是区间和)

inline void build(int l,int r,int root)
{
    if (l==r){tree[root]=x[l];return;}
    int mid=(l+r)/2;
    build(l,mid,root*2);
    build(mid+1,r,root*2+1);
    tree[root]=tree[root*2]+tree[root*2+1];
}

单点修改

x为修改的位置,ans为值,l和r为线段(树)区间。

如果最右端比查询位置左或最左端比查询位置右,就退出(废话真多)

如果查到了(为叶节点且是该位置)就修改此值。

最后递归然后更新值。

inline void updata(int l,int r,int root,int x,int ans)
{
    if(r<x||l>x) return;
    if(l==r&&l==x){tree[root]+=ans;return;}
    int mid=(l+r)/2;
    updata(l,mid,root*2,x,ans);
    updata(mid+1,r,root*2+1,x,ans);
    tree[root]=tree[root*2]+tree[root*2+1];
}

查询答案

inline long long search(int maxl,int maxr,int root,int l,int r)
{
    if (maxl>r||maxr<l) return 0;
    if (l<=maxl&&maxr<=r) return tree[root];
    int mid=(maxl+maxr)/2;
    return search(maxl,mid,root*2,l,r)+search(mid+1,maxr,root*2+1,l,r);
}

区间修改

蒟蒻不会....

才怪

#include<bits/stdc++.h>
#define MAXN 1000005
using namespace std;
long long tree[4*MAXN],add[4*MAXN],n,num[MAXN],m;
inline void build(long long l,long long r,long long root)
{
    if (l==r){tree[root]=num[l];return;}
    long long mid=(l+r)/2;
    build(l,mid,root*2);
    build(mid+1,r,root*2+1);
    tree[root]=tree[root*2]+tree[root*2+1];
}
inline void updata(long long l,long long r,long long root,long long x,long long ans)
{
    if(r<x||l>x) return;
    if(l==r&&l==x){tree[root]+=ans;return;}
    long long mid=(l+r)/2;
    updata(l,mid,root*2,x,ans);
    updata(mid+1,r,root*2+1,x,ans);
    tree[root]=tree[root*2]+tree[root*2+1];
}
inline void modify(long long root,long long maxl,long long maxr,long long l,long long r,long long v)
{
    if (maxl>=l&&maxr<=r){add[root]+=v;return;}
    tree[root]+=(min(maxr,r)-max(maxl,l)+1)*v;
    long long mid=(maxl+maxr)/2;
    if (l<=mid) modify(root*2,maxl,mid,l,r,v);
    if (mid<r) modify(root*2+1,mid+1,maxr,l,r,v);
}
inline long long search(long long maxl,long long maxr,long long root,long long l,long long r)
{
    if (maxl>r||maxr<l) return 0;
    if (l<=maxl&&maxr<=r) return tree[root]+(maxr-maxl+1)*add[root];
    long long mid=(maxl+maxr)/2;
    long long res=(min(maxr,r)-max(maxl,l)+1)*add[root];
    if (l<=mid) res+=search(maxl,mid,root*2,l,r);
    if (mid<r) res+=search(mid+1,maxr,root*2+1,l,r);
    return res;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for (long long i=1;i<=n;i++) scanf("%lld",&num[i]);
    build(1,n,1);
    for (long long i=1;i<=m;i++)
    {
        long long t,x,y,k;
        scanf("%lld",&t);
        if (t==1)
        {
            scanf("%lld%lld%lld",&x,&y,&k);
            modify(1,1,n,x,y,k);
        }
        else
        {
            scanf("%lld%lld",&x,&y);
            cout<<search(1,n,1,x,y)<<endl;
        }
    }
    return 0;
}

返回下标

只放代码了~~~

就是加个结构体

#include<bits/stdc++.h>
using namespace std;
struct node{int id,minn;};
node tree[4*105];
int n,m,x[10005],wz;
inline void build(int l,int r,int root)
{
    if (l==r){tree[root].minn=x[l];tree[root].id=l;return;}
    int mid=(l+r)/2;
    build(l,mid,root*2);
    build(mid+1,r,root*2+1);
    if (tree[root*2].minn>=tree[root*2+1].minn) tree[root].id=tree[root*2+1].id,tree[root].minn=tree[root*2+1].minn;
	else tree[root].id=tree[root*2].id,tree[root].minn=tree[root*2].minn;
}
inline int search(int maxl,int maxr,int root,int l,int r)
{
    if (maxl>r||maxr<l) return 0;
    if (l<=maxl&&maxr<=r){wz=tree[root].id;return tree[root].minn;}
    int mid=(maxl+maxr)/2;
    return min(search(maxl,mid,root*2,l,r),search(mid+1,maxr,root*2+1,l,r));
}
inline void updata(int l,int r,int root,int x,int ans)
{
    if(r<x||l>x) return;
    if(l==r&&l==x){tree[root].minn+=ans;return;}
    int mid=(l+r)/2;
    updata(l,mid,root*2,x,ans);
    updata(mid+1,r,root*2+1,x,ans);
    if (tree[root*2].minn>=tree[root*2+1].minn) tree[root].id=tree[root*2+1].id,tree[root].minn=tree[root*2+1].minn;
	else tree[root].id=tree[root*2].id,tree[root].minn=tree[root*2].minn;
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%d",&x[i]);
	build(1,m,1);
	cout<<search(1,m,1,1,m)<<endl;
	cout<<wz<<endl;
	return 0;
}

例题

Luogu P1198 [JSOI2008]最大数

转载于:https://www.cnblogs.com/GREED-VI/p/9588761.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值