线段树浅谈

                                            线段树浅谈

    为了维护某些数据,支持查询和更新数据。

    维护的数据具有区间可加性的性质。(如连续和,区间最值)

    一般n个数据,线段树数组大小开n<<2;

 rt;        //节点
rt<<1;     //左节点
rt<<1|1;   //右节点
#define MAXN 1e5
struct Tree{
  int l;
  int r;
  int sum;
} node[MAXN<<2];

更新:

void Pushup(int rt)                      //更新
{
    node[rt].Sum=node[rt<<1].Sum+node[rt<<1|1].Sum;
}

建树:


void Bulid(int L,int R,int rt)            //建树
{
  node[rt].l=L;
  node[rt].r=R;
  node[rt].Sum=0;
  if(L==R)
  {
      node[rt].Sum=a[L];
      return;
  }
  int m=(L+R)>>1;
  Bulid(L,m,rt<<1);       //把rt左节点建好(可能此过程中还存在递归)
  Bulid(m+1,R,rt<<1|1);   //把rt右节点建好(可能此过程中还存在递归)
  Pushup(rt);
}

节点更新:

void Update1(int loc,int val,int rt)          //单点更新
{
  if(node[rt].l==node[rt].r)           //等于的时候一定是loc
  {
      node[rt].Sum=val;
      return;
  }
  int m=(node[rt].l+node[rt].r)>>1;
  if(m<loc)  Update1(loc,val,rt<<1|1);
  else       Update1(loc,val,rt<<1);
  Pushup(rt);
}

区间更新:

void Update2(int L,int R,int val,int rt)
{
    if(L<=node[rt].l&&node[rt].r<=R)
    {
        node[rt].Sum+=(node[rt].r-node[rt].l+1)*val;
        return;
    }
    int m=(node[rt].l+node[rt].r)>>1;
    if(L<=m)  Update2(L,R,val,rt<<1);
    if(R>m)   Update2(L,R,val,rt<<1|1);
    Pushup(rt);
}

查询:

int Query(int L,int R,int rt)         //查询操作
{
    if(L<=node[rt].l&&node[rt].r<=R)
    {
        return node[rt].Sum;
    }
    int ans=0;
    int m=(node[rt].l+node[rt].r)>>1;
    if(L<=m) ans+=Query(L,R,rt<<1);       //把左儿子区间中含有 (L,R)的部分加起来
    if(R>m)  ans+=Query(L,R,rt<<1|1);     //把右儿子区间中含有 (L,R)的部分加起来
    return ans;
}

主函数调用过程:

int main()
{
  Bulid(1,n,1);
  Update1(loc,val,rt);
  Update2(L,R,val,rt);
  Query(L,R,1);
}

对应题目:

Description

因为聪明的你帮averyboy解决了荟园阿姨给他的问题,他顺利获得了免费的午餐。这次,averyboy再次来到了荟园,还是那个窗口,还是那位阿姨。。。。。。这次,阿姨不想那么容易让他吃到免费的午餐,所以给出了比上次要难的问题。这不,averyboy又来 求助你了。阿姨这次给出的问题如下.给你一个数组a[1]~a[N],然后给你q次操作,每次操作有三种类型,第一种类型是把数组中的某一个数改为x,第二种操作是询问区间[l, r]之间所有奇数的和,如果这个区间没有奇数,输出0,第三种操作是询问区间[l, r]之间所有偶数的和,如果这个区间没有偶数,输出0.

Input

第一行一个整数T,代表测试数据的组数.
接下来T组测试数据.
每组测试数据第一行两个整数N, q(N, q <= 1e5),分别代表数组a的长度和操作的个数
接下来一行为N个整数,代表数组a
接下来q行,每一行格式为下面三种格式中的一种
1 loc x 将loc位置的数改为x,即a[loc] = x(1 <= loc <= N, 1 <= x <= 1e5)
2 l r 询问区间[l, r]之间奇数的和(1 <= l <= r <= N)
3 l r 询问区间[l, r]之间偶数的和(1 <= l <= r <= N)

Output

对于每组测试数据的询问操作,输出结果

Sample Input

1
5 4
1 2 3 4 5
2 1 5
3 1 5
1 1 2
3 1 5

Sample Output

9
6
8

代码:

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
int a[MAXN];
 int N,q;
struct A{
  int l;
  int r;
  int Sum1;
  int Sum2;
} node[MAXN<<2];

void Pushup(int rt)                      //更新
{
    node[rt].Sum1=node[rt<<1].Sum1+node[rt<<1|1].Sum1;
    node[rt].Sum2=node[rt<<1].Sum2+node[rt<<1|1].Sum2;
}

void Bulid(int L,int R,int rt)            //建树
{
  node[rt].l=L;
  node[rt].r=R;
  node[rt].Sum1=0;
  node[rt].Sum2=0;
  if(L==R)
  {
      if(a[L]%2==0)
        node[rt].Sum1=a[L];
      else
        node[rt].Sum2=a[L];
      return; 
  }
  int m=(L+R)>>1;
  Bulid(L,m,rt<<1);       //把rt左节点建好(可能此过程中还存在递归)
  Bulid(m+1,R,rt<<1|1);   //把rt右节点建好(可能此过程中还存在递归)
  Pushup(rt);
}

void Update1(int loc,int val,int rt)          //单点更新
{
  if(val%2==0&&a[loc]%2==0)
  {
    if(node[rt].l==node[rt].r)           //等于的时候一定是loc
    {
        node[rt].Sum1=val;
      //   node[rt].Sum2=0;
        return;
    }
    int m=(node[rt].l+node[rt].r)>>1;
    if(m<loc)  Update1(loc,val,rt<<1|1);
    else       Update1(loc,val,rt<<1);
    Pushup(rt);
  }
   if(val%2==1&&a[loc]%2==1)
  {
    if(node[rt].l==node[rt].r)           //等于的时候一定是loc
    {
        node[rt].Sum2=val;
        //  node[rt].Sum1=0;
        return;
    }
    int m=(node[rt].l+node[rt].r)>>1;
    if(m<loc)  Update1(loc,val,rt<<1|1);
    else       Update1(loc,val,rt<<1);
    Pushup(rt);
  }
   if(val%2==0&&a[loc]%2==1)
  {
    if(node[rt].l==node[rt].r)           //等于的时候一定是loc
    {
        node[rt].Sum1=val;
        node[rt].Sum2=0;
        return;
    }
    int m=(node[rt].l+node[rt].r)>>1;
    if(m<loc)  Update1(loc,val,rt<<1|1);
    else       Update1(loc,val,rt<<1);
    Pushup(rt);
  }
   if(val%2==1&&a[loc]%2==0)
  {
    if(node[rt].l==node[rt].r)           //等于的时候一定是loc
    {
        node[rt].Sum2=val;
        node[rt].Sum1=0;
        return;
    }
    int m=(node[rt].l+node[rt].r)>>1;
    if(m<loc)  Update1(loc,val,rt<<1|1);
    else       Update1(loc,val,rt<<1);
    Pushup(rt);
  }
  a[loc]=val;               //每次都变了,所以有46和59行
}



int Query(int L,int R,int rt,int num)         //查询操作
{
    if(num==3)
    {
      if(L<=node[rt].l&&node[rt].r<=R)
      {
          return node[rt].Sum1;
      }
      int ans=0;
      int m=(node[rt].l+node[rt].r)>>1;
      if(L<=m) ans+=Query(L,R,rt<<1,num);       //把左儿子区间中含有 (L,R)的部分加起来
      if(R>m)  ans+=Query(L,R,rt<<1|1,num);     //把右儿子区间中含有 (L,R)的部分加起来
      return ans;
    }
     if(num==2)
    {
      if(L<=node[rt].l&&node[rt].r<=R)
      {
          return node[rt].Sum2;
      }
      int ans=0;
      int m=(node[rt].l+node[rt].r)>>1;
      if(L<=m) ans+=Query(L,R,rt<<1,num);       //把左儿子区间中含有 (L,R)的部分加起来
      if(R>m)  ans+=Query(L,R,rt<<1|1,num);     //把右儿子区间中含有 (L,R)的部分加起来
      return ans;
    }
}



int main()
{
    int T;
    cin>>T;
    while(T--)
    {
      cin>>N>>q;
      for(int i=1;i<=N;i++)
        cin>>a[i];
      Bulid(1,N,1);
      while(q--)
      {
          int num;
          int a,b;
          cin>>num>>a>>b;
          if(num==1)
            Update1(a,b,1);
          else if(num==2)
            cout<<Query(a,b,1,num)<<endl;
          else if(num==3)
            cout<<Query(a,b,1,num)<<endl;
      }
    }
    return 0;
}

Description

为了检验你上午有没有好好听我讲课,于是有了这一题。给你一个数组a[1]~a[N],有q次操作,每次操作有两种类型,第一种询问操作,询问区间[l, r]之间数的和,第二种操作是修改操作,将位置loc的数改为x。对,你没有听错,就是这么简单,简单的话就赶快ac吧。

Input

第一行一个整数T,代表测试数据的组数.
接下来T组测试数据.
每组测试数据第一行两个整数N, q(N, q <= 1e5),分别代表数组a的长度和操作的个数
接下来一行为N个整数,代表数组a
接下来q行,每一行格式为下面两种格式中的一种
1 loc x 将loc位置的数改为x,即a[loc] = x(1 <= loc <= N, 1 <= x <= 1e5)
2 l, r 询问区间[l, r]之间数的和(1 <= l <= r <= N)

Output

对于每组测试数据的询问操作,输出结果

Sample Input

1
5 3
1 2 3 4 5
2 1 2
1 1 100
2 1 2

Sample Output

3
102

代码:

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
int a[MAXN];
struct A{
  int l;
  int r;
  int Sum;
} node[MAXN<<2];
 
void Pushup(int rt)                      //更新
{
    node[rt].Sum=node[rt<<1].Sum+node[rt<<1|1].Sum;
}
 
void Bulid(int L,int R,int rt)            //建树
{
  node[rt].l=L;
  node[rt].r=R;
  node[rt].Sum=0;
  if(L==R)
  {
      node[rt].Sum=a[L];
      return;
  }
  int m=(L+R)>>1;
  Bulid(L,m,rt<<1);       //把rt左节点建好(可能此过程中还存在递归)
  Bulid(m+1,R,rt<<1|1);   //把rt右节点建好(可能此过程中还存在递归)
  Pushup(rt);
}
 
void Update1(int loc,int val,int rt)          //单点更新
{
  if(node[rt].l==node[rt].r)           //等于的时候一定是loc
  {
      node[rt].Sum=val;
      return;
  }
  int m=(node[rt].l+node[rt].r)>>1;
  if(m<loc)  Update1(loc,val,rt<<1|1);
  else       Update1(loc,val,rt<<1);
  Pushup(rt);
}
 
void Update2(int L,int R,int val,int rt)
{
    if(L<=node[rt].l&&node[rt].r<=R)
    {
        node[rt].Sum+=(node[rt].r-node[rt].l+1)*val;
        return;
    }
    int m=(node[rt].l+node[rt].r)>>1;
    if(L<=m)  Update2(L,R,val,rt<<1);
    if(R>m)   Update2(L,R,val,rt<<1|1);
    Pushup(rt);
}
 
int Query(int L,int R,int rt)         //查询操作
{
    if(L<=node[rt].l&&node[rt].r<=R)
    {
        return node[rt].Sum;
    }
    int ans=0;
    int m=(node[rt].l+node[rt].r)>>1;
    if(L<=m) ans+=Query(L,R,rt<<1);       //把左儿子区间中含有 (L,R)的部分加起来
    if(R>m)  ans+=Query(L,R,rt<<1|1);     //把右儿子区间中含有 (L,R)的部分加起来
    return ans;
}
 
 
 
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
      int N,q;
      cin>>N>>q;
      for(int i=1;i<=N;i++)
        cin>>a[i];
      Bulid(1,N,1);
      while(q--)
      {
          int num;
          int a,b;
          cin>>num>>a>>b;
          if(num==1)
            Update1(a,b,1);
          else if(num==2)
            cout<<Query(a,b,1)<<endl;
      }
    }
    return 0;
}

Description

又到了社团招新的时候,荟园主干道上有很多社团在摆点,averyboy来到了这里,在averyboy心中每个社团有一个满意度。我们可以想象这些社团招新点排成一排,编号从1-N,averyboy又定义了一个区间[l, r]的价值为在这段区间中所有社团满意度的最大值Max和最小值Min的差值即Max - Min.现在averyboy给了你一个问题。一共有q次操作,每次操作有两种类型,第一种类型为查询操作,查询区间[l, r]之间的价值。第二种是更改操作,将第i个社团招新点的满意度更改为x.

Input

第一行一个整数T,代表测试数据组数
接下来T组测试数据,每组测试数据第一行为两个整数N,q(N, q <= 1e5)分别代表社团招新点的个数和操作的个数
接下来一行有N个数,代表N个社团招新点的初始满意度
接下来q行,每一行格式如下面两种中的一种
1 loc x 代表更改操作,将第loc号社团招新点的满意度改为x(1 <= loc <= N, 1 <= x <= 1e5)
2 l r 代表查询操作,查询区间[l, r]的价值(l 1 <= l <= r <= N)

Output

对于每组数据的查询操作,输出结果.

Sample Input

1
5 3
1 2 3 4 5
2 1 5
1 1 2
2 1 5

Sample Output

4
3

代码:

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
int a[MAXN];
struct A{
  int l;
  int r;
  int Max;
  int Min;
} node[MAXN<<2];
 
void Pushup(int rt)                      //更新
{
   if(node[rt<<1].Max>node[rt<<1|1].Max)
      node[rt].Max=node[rt<<1].Max;
   else
      node[rt].Max=node[rt<<1|1].Max;
   if(node[rt<<1].Min>node[rt<<1|1].Min)
      node[rt].Min=node[rt<<1|1].Min;
   else
      node[rt].Min=node[rt<<1].Min;
}
 
void Bulid(int L,int R,int rt)            //建树
{
  node[rt].l=L;
  node[rt].r=R;
  if(L==R)
  {
      node[rt].Max=a[L];
      node[rt].Min=a[L];
      return;
  }
  int m=(L+R)>>1;
  Bulid(L,m,rt<<1);       //把rt左节点建好(可能此过程中还存在递归)
  Bulid(m+1,R,rt<<1|1);   //把rt右节点建好(可能此过程中还存在递归)
  Pushup(rt);
}
 
void Update1(int loc,int val,int rt)          //单点更新
{
    if(node[rt].l==node[rt].r)
    {
        node[rt].Max=val;
        node[rt].Min=val;
        return ;
    }
    int m=(node[rt].l+node[rt].r)>>1;
    if(m<loc)  Update1(loc,val,rt<<1|1);
    else       Update1(loc,val,rt<<1);
    Pushup(rt);
}
 
 
int Query1(int L,int R,int rt)         //查询操作
{
  if(L<=node[rt].l&&node[rt].r<=R)
    return node[rt].Max;
  int a,b;
  int flag1=0,flag2=0;
  int m=(node[rt].l+node[rt].r)>>1;
  if(L<=m) {a=Query1(L,R,rt<<1);flag1++;}       //把左儿子区间中含有 (L,R)的部分加起来
  if(R>m)  {b=Query1(L,R,rt<<1|1);flag2++;}     //把右儿子区间中含有 (L,R)的部分加起来
  if(flag2==0)  return a;
  else if(flag1==0)  return b;
  else if(flag1&&flag2)
  {
      if(a>b) return a;
      else return b;
  }
}
 
int Query2(int L,int R,int rt)         //查询操作
{
  if(L<=node[rt].l&&node[rt].r<=R)
    return node[rt].Min;
  int a,b;
  int flag1=0,flag2=0;
  int m=(node[rt].l+node[rt].r)>>1;
  if(L<=m) {a=Query2(L,R,rt<<1);flag1++;}       //把左儿子区间中含有 (L,R)的部分加起来
  if(R>m)  {b=Query2(L,R,rt<<1|1);flag2++;}   //把右儿子区间中含有 (L,R)的部分加起来
  if(flag2==0)  return a;
  else if(flag1==0)  return b;
  else if(flag1&&flag2)
  {
      if(a>b) return b;
      else return a;
  }
}
 
int Find(int L,int R,int rt)
{
    return Query1(L,R,rt)-Query2(L,R,rt);
}
 
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
      int N,q;
      cin>>N>>q;
      for(int i=1;i<=N;i++)
        cin>>a[i];
      Bulid(1,N,1);
      while(q--)
      {
          int num,a,b;
          cin>>num>>a>>b;
          if(num==1)
            Update1(a,b,1);
          else if(num==2)
            cout<<Find(a,b,1)<<endl;
      }
    }
    return 0;
}

未完:

主席树

可持续化字典树

伸展树

猜你喜欢

转载自blog.csdn.net/hzaukotete/article/details/81087640