线段树~总结下吧

为什么我比较喜欢线段树呢

因为线段树容易写,万一写错了还容易调试

能以优秀的效率完成一些比较水的操作

如:

单点更新(增减, 替换等)O(logn)

查询区间(最值,求和等 )O(logn)

但只能查询区间的话明显是不够的

而修改区间用单点更新的方法复杂度就不再优了

于是我们引进一种lazy标记, 也可以叫可持续化

这个时候再谈修改区间, 无论是替换, 增减, 求和, 异或就无压力了

至于区间的合并与离散化扫描线也不在话下


贴段code吧

我习惯写我最爱的大常数版堆式线段树

simple

#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;

const int maxn = 100000 + 5;

class Interval_tree
{
   public :
      Interval_tree()
      {
         memset(a, 0, sizeof(a));
         memset(l, 0, sizeof(l));
         memset(r, 0, sizeof(r));
         memset(lazy, 0, sizeof(lazy));
      }

      void pushup(int rt)
      {
         a[rt] = a[rt << 1] + a[rt << 1 | 1];
      }

      void pushdown(int rt, int wh)
      {
         if (lazy[rt] != 0)
         {
            lazy[rt << 1] += lazy[rt];
            lazy[rt << 1 | 1] += lazy[rt];
            a[rt << 1] += lazy[rt] * (wh - (wh >> 1));
            a[rt << 1 | 1] += lazy[rt] * (wh >> 1);
            lazy[rt] = 0;
         }
      }

      void buildtree(int rt)
      {
         if (l[rt] == r[rt])
         {
            scanf("%lld", &a[rt]);
            return;
         }
         int mid = (l[rt] + r[rt]) >> 1;
         l[rt << 1] = l[rt];
         r[rt << 1] = mid;
         l[rt << 1 | 1] = mid + 1;
         r[rt << 1 | 1] = r[rt];
         if (l[rt] <= mid) buildtree(rt << 1);
         if (r[rt] > mid) buildtree(rt << 1 | 1);
         pushup(rt);
      }

      void Insert(int x, int y, int k, int rt)
      {
         if (x <= l[rt] && y >= r[rt])
         {
            lazy[rt] += k;
            a[rt] += (long long)k * (r[rt] - l[rt] + 1);
            return;
         }
         pushdown(rt, r[rt] - l[rt] + 1);
         int mid = (l[rt] + r[rt]) >> 1;
         if (x <= mid) Insert(x, y, k, rt << 1);
         if (y > mid) Insert(x, y, k, rt << 1 | 1);
         pushup(rt);
      }

      long long Query(int x, int y, int rt)
      {
         if (x <= l[rt] && y >= r[rt])
            return a[rt];
         pushdown(rt, r[rt] - l[rt] + 1);
         int mid = (l[rt] + r[rt]) >> 1;
         long long ans = 0;
         if (x <= mid) ans += Query(x, y, rt << 1);
         if (y > mid) ans += Query(x, y, rt << 1 | 1);
         return ans;
      }

      void initialize()
      {
         cin >> n >> m;
         l[1] = 1; r[1] = n;
         buildtree(1);
         for (int i = 1; i <= m; ++i)
         {
            char ch; int x, y, k;
            scanf("\n%c", &ch);
            if (ch == 'Q')
            {
               scanf("%d%d", &x, &y);
               long long Ans = Query(x, y, 1);
               printf("%lld\n", Ans);
            }
            else if (ch == 'C')
            {
               scanf("%d%d%d", &x, &y, &k);
               Insert(x, y, k, 1);
            }
         }
      }

   private :
      int n, m;
      long long a[maxn << 2];
      int l[maxn << 2], r[maxn << 2];
      long long lazy[maxn << 2];
};
Interval_tree interval_tree;

int main()
{
   freopen("simple.in", "r", stdin);
   freopen("simple.out", "w", stdout);
   interval_tree.initialize();
   return 0;
}

另吐槽下cuizimu奇葩的类写法, 在类内声明, 要封装的函数全写外面。

且还用drj的写法和我一样的神D来D我。


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值