树状数组和线段树

Posted 中二病没有蛀牙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树状数组和线段树相关的知识,希望对你有一定的参考价值。

树状数组

  1. 某个位置加上一个位置(单点修改) O ( l o g n ) O(log_n) O(logn

  2. 求某一个前缀和 (区间查询) O ( l o g n ) O(log_n) O(logn
    c ( x ) = c ( x ) + c ( l o w b i t ( x ) + … … ) c(x) = c(x) + c(lowbit(x)+……) c(x)=c(x)+c(lowbit(x)+)

l o w b i t ( x ) = x & − x lowbit(x) = x\\&-x lowbit(x)=x&x
树状数组公式: c ( x ) = ( x − l o w b i t ( x ) , x ] c(x) = (x - lowbit(x),x] c(x)=(xlowbit(x),x]

板子题

https://www.acwing.com/problem/content/1266/

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int s[maxn],tr[maxn];
int n,m,a,b,k;

int  lowbit(int x){
    return x & -x;
}

void add(int x,int v)
{
    for(int i = x; i <= n;i += lowbit(i))
        tr[i] +=v ;
}
int querry(int x){
    int  res = 0;
    for(int i = x;i ;i -= lowbit(i))
        res += tr[i];
    return res;
}

int main ()
{
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i= 1;i <= n; ++i) 
    {   
        cin>>s[i];
        add(i,s[i]);
    }
    while(m--){
        cin>>k>>a>>b;
        if(k == 1){
            add(a,b);
        }
        if(k == 0)
            cout<<querry(b)-querry(a - 1)<<endl;
    }
    return 0;
}

经典例题数星星

https://www.acwing.com/problem/content/1267/

#include<bits/stdc++.h>
using namespace std;
const int maxn = 32005;
typedef pair<int ,int> PII;
int tr[maxn];
int n;
int ans[maxn];

int lowbit(int x){
    return x & -x;
}

void add(int x,int v)
{
    for(int i = x; i < maxn ; i += lowbit(i)){
        tr[i] +=v ;
    }
}

int querry(int x){
    int res = 0;
    for(int i = x;i; i-= lowbit(i)){
        res += tr[i];
    }
    return res; 
}

int main()
{
    int x,y,ny,nx;
    cin>>n;
    for(int  i = 1; i<= n;i++){
        cin>>x>>y;
        x++;
        int t = querry(x);
        ans[t]++;
        add(x,1);
    }
    for(int i = 0 ;i < n;i++)
        cout<<ans[i]<<endl;
    return 0;
}

线段树

  1. 单点修改
  2. 区间查询 O ( l o g n ) O(log_n) O(logn
  3. 区间修改(懒标记)

操作:

  1. pushup
  2. build
  3. modify
  4. querry

板子题

和上面的树状数组是同一个题

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int s[maxn];
int n,m;

struct  node
{
    int l,r;
    int sum;
    /* data */
}tr[maxn*4];


void pushup(int u){
    tr[u].sum = tr[u<<1].sum + tr[u << 1 | 1].sum;
}

void build(int u,int l,int r){
    if(l == r) tr[u] = {l,r,s[r]};
    else{
        tr[u] = {l,r};
        int mid = l +r >>1;
        build(u<<1,l,mid),build(u<<1|1,mid+1,r);
        pushup(u);
    }

}

void modify(int u,int x,int v)
{
    if(tr[u].l == tr[u].r) tr[u].sum += v;
    else{
        int mid= tr[u].l + tr[u].r >> 1;
        if(x <= mid) modify(u<<1,x,v);
        if(x > mid) modify(u<<1|1,x,v);
        pushup(u);
    }
}

int querry(int u,int l,int r){
    if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
    int mid = tr[u].l + tr[u].r >>1;
    int sum = 0;
    if(l <= mid) sum += querry(u<<1,l,r);
    if(r > mid) sum += querry(u<<1 |1,l,r);
    return sum;
}
int main ()
{
    ios::sync_with_stdio(false);
    cin>>n>>m;
    int k,a,b;
    for(int i= 1;i <= n; ++i) 
    {   
        cin>>s[i];
    }
    build(1,1,n);
    while(m--){
        cin>>k>>a>>b;
        if(k == 1){
            modify(1,a,b);
        }
        if(k == 0)
            cout<<querry(1,a,b)<<endl;;
    }
    return 0;
}

数列区间最大值

https://www.acwing.com/problem/content/1272/
线段树维护最大值

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
typedef long long ll ;
int a[maxn];
int n,m;

struct  node
{
    int l,r;
    int ma;
}tr[4*maxn];

void pushup(int u)
{
    tr[u].ma = max(tr[u<<1].ma,tr[u<<1 | 1].ma);

}

void build(int u,int l,int r){
    if(l  == r) tr[u] = {l,r,a[r]};
    else{
        tr[u] = {l,r};
        int mid = l +r >> 1;
        build(u << 1,l,mid);
        build(u << 1|1,mid+1,r);
        pushup(u);
    }
}

int querry(int u ,int l,int r){
    if( l <= tr[u].l &&r >= tr[u].r) return tr[u].ma;
    int mid = tr[u].l+tr[u].r >> 1;
    int ma = 0;
    if(l <= mid)
        ma = querry(u << 1,l,r);
    if(r > mid) ma = max(ma,querry(u << 1|1,l,r));
    return ma;
}

int main()
{
    // ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i = 1;i <= n;++i)
        scanf("%d",&a[i]);
    build(1,1,n);
    int x,y;
    while (m--)
    {
        scanf("%d %d",&x,&y);
        printf("%d\\n",querry(1,x,y));
    }

    return 0;
}

以上是关于树状数组和线段树的主要内容,如果未能解决你的问题,请参考以下文章

吊打线段树的超级树状数组

Leetcode-线段树和树状数组

线段树与树状数组的对比应用

线段树&树状数组

树状数组

树状数组板子