• 线段树那些事


    线段树是学不明白了……

    部分指针用法

    对于这段代码,

    struct Node{
        int a, b, c;
    }YJH[100], x;
    Node *p = YJH, *q = &x;
    

    以下代码在使用过程中是等价的:


    cout << x.a << endl;
    cout << q->a << endl;
    cout << (*q).a << endl;
    

    cout << YJH[2].b << endl;
    cout << *(p + 2).b << endl;
    

    用指针写线段树

    相对于上一次线段树的写法,我获得了用指针写线段树的本领。

    相对于用下标来写线段树,指针的代码量、可扩展性都变好了。各种函数分别用下表和指针的不同写法在下面会陈列出来。

    初始化

    数组:

    #define N 1000050//数据边界
    struct tree{
    	long long l, r, sum, tag, tag_x;
        //l,r左右端点,sum为结点对应区间和,tag为加法标记,tag_x为乘法标记 
    }t[N];//线段树 
    long long a[N];//输入的数列(1~n) 
    long long m, n, p, k;//如题意(k是操作种类) 
    long long ls(long long rt){return rt << 1;}//左孩子 
    long long rs(long long rt){return rt << 1 | 1;}//右孩子 lcez_yjh
    

    指针:

    struct Node{
      int l, r;
      ll v, tag;
      Node *ls, *rs;
    
      inline void () {} 
      inline void () {} 
      inline void () {} 
      inline void () {} 
        
    }Mem[maxn << 1];
    
    Node *pool = Mem;
    

    相对来讲,指针的实现方式代码更简洁,而且在最后一层不会浪费过多的空间。线段树开空间的时候需要对极端情况进行考虑,数组往往开到maxn << 2,但是在指针这里只需要开(maxn << 1) - 1

    核心函数:FindRange

    数组:

    void build(long long rt, long long l, long long r) {
      if (t[rt].l <= l && t[rt].r >= r) { 
    	do sth.
    	return;
      }
      long long mid = (t[rt].l + t[rt].r) >> 1;
      if (l <= mid)
        build(ls(rt), l, r);
      if (r > mid)
        build(rs(rt), l, r);
      pushup(rt);
    }
    

    指针:

    void FindRange(L, R) {
      if (InRange(L, R)) {
        do sth.
        return;
      }
      else if (OutofRange(L, R)) return; 
      else {
        ls->FindRange(L, R);
        rs->FindRange(L, R);
      }
      pushup();
    }
    

    可以看到,直接用指针指向左右儿子的方法不受到数组下表的限制,所以就不会有智障的四倍空间,只需要按照实际空间大小开就可以了。

    内存池

    用指针建立线段树的方法大致有两种,一种是直接在建树的时候开一个新空间Node *u = new Node,但是new这个语句常数十分大,运行起来巨慢。所以我们可以提前申请好一个Node Mem[maxn << 1 - 1]的数组,并用一个指针Node *Pool = Mem进行取出新点,从而实现代替new功能的函数:

    Node* New() { return ++Pool; }
    

    完整代码:

    数组:

    #include <bits/stdc++.h>
    #define N 1000050
    using namespace std;
    
    struct tree{
      long long l, r, sum, tag, tag_x;
      //l,r左右端点,sum为结点对应区间和,tag为加法标记,tag_x为乘法标记 
    }t[N];//线段树 
    long long a[N];//输入的数列(1~n) 
    long long m, n, p = 9223372036854775807, k;//如题意(k是操作种类) 
    long long ls(long long rt){return rt << 1;}//左孩子 
    long long rs(long long rt){return rt << 1 | 1;}//右孩子 
    
    void build(long long rt, long long l, long long r) {
      t[rt].tag_x = 1; t[rt].tag = 0;//初始化
      t[rt].l = l, t[rt].r = r;//建立一个结点,更新左右端点标记 
      if (l == r) { //如果到了叶子结点 
    	t[rt].sum = a[l] % p; //不要忘记取模操作 
    	return;
      }
      long long mid = (l + r) >> 1; //中间节点 
      build(ls(rt), l, mid);
      build(rs(rt), mid + 1, r); //如果不是叶子结点,就分别建立左右孩子 
      t[rt].sum = (t[ls(rt)].sum + t[rs(rt)].sum) % p; // 更新sum 
    }
    
    void push_down(long long rt) {
      t[ls(rt)].tag_x = (t[ls(rt)].tag_x * t[rt].tag_x) % p;
      t[rs(rt)].tag_x = (t[rs(rt)].tag_x * t[rt].tag_x) % p;//乘法懒标记更新后取模 
    
      t[ls(rt)].tag = (t[ls(rt)].tag * t[rt].tag_x) % p;
      t[rs(rt)].tag = (t[rs(rt)].tag * t[rt].tag_x) % p;//加法懒标记更新 
    
      t[ls(rt)].sum = (t[ls(rt)].sum * t[rt].tag_x) % p;
      t[rs(rt)].sum = (t[rs(rt)].sum * t[rt].tag_x) % p;//sum结点对应区间和更新
    
      t[rt].tag_x = 1; //父亲的标记已经下传,就归零(因为是乘法,所以要调到1) 
    	
      t[ls(rt)].tag = (t[ls(rt)].tag + t[rt].tag) % p;
      t[rs(rt)].tag = (t[rs(rt)].tag + t[rt].tag) % p;//加法懒标记更新 
    	
      t[ls(rt)].sum += (t[ls(rt)].r - t[ls(rt)].l + 1) * t[rt].tag;
      t[rs(rt)].sum += (t[rs(rt)].r - t[rs(rt)].l + 1) * t[rt].tag;//sum结点对应区间和更新
        
      t[rt].tag = 0;//父亲的标记已经下传,就归零 
    }
    
    void change(long long rt, long long x, long long y, long long z) {
      if (x <= t[rt].l && y >= t[rt].r) {
    	t[rt].tag = (t[rt].tag + z) % p;
    	t[rt].sum = (t[rt].sum + (t[rt].r - t[rt].l + 1) * z) % p; //如果修改区间覆盖了这个节点的区间,就更新 
    	return;
      }
      if(t[rt].tag || t[rt].tag_x != 1) push_down(rt);//访问孩子结点的时候一定先把懒标记 传下去 
      long long mid = (t[rt].l + t[rt].r) >> 1;
      if (x <= mid) {
    	change(ls(rt),x,y,z);
      }
      if(y > mid){
    	change(rs(rt),x,y,z);
      }
      //分别往左右儿子传 
    
      t[rt].sum = (t[ls(rt)].sum + t[rs(rt)].sum) % p; //维护 
    }
    
    void change_x(long long rt, long long x, long long y, long long z) {
      if(x <= t[rt].l && y >= t[rt].r){
    	t[rt].tag_x = (t[rt].tag_x * z) % p;
    	t[rt].sum = (t[rt].sum * z) % p;
    	t[rt].tag = (t[rt].tag * z) % p;//如果修改区间覆盖了这个节点的区间,就更新 
    	return;
      }
      if(t[rt].tag || t[rt].tag_x != 1) push_down(rt);//访问孩子结点的时候一定先把懒标记 传下去 
      long long mid = (t[rt].l + t[rt].r) >> 1;
      if (x <= mid) {
    	change_x(ls(rt), x, y, z);
      }
      if (y > mid) {
    	change_x(rs(rt), x, y, z);
      }
      //分别往左右儿子传 
      t[rt].sum = (t[ls(rt)].sum + t[rs(rt)].sum) % p; //维护
    }
    
    long long getsum(long long rt, long long x, long long y) {
      long long res = 0;
      if (x <= t[rt].l && y >= t[rt].r) {
    	return t[rt].sum % p;
      }
      if (t[rt].tag || t[rt].tag_x != 1) push_down(rt);
      long long mid = (t[rt].r + t[rt].l) >> 1;
      if (x <= mid) {
    	res += getsum(ls(rt), x, y);
      }
      if (y > mid) {
    	res += getsum(rs(rt), x, y);
      }
      return res % p;
    }
    
    int main() {
      long long i, j, x, y, z;
      scanf("%lld%lld%lld", &n, &m, &p);
      //scanf("%lld%lld", &n, &m); 
      for (i = 1; i <= n; i++) {
    	scanf("%lld", &a[i]);
      }
      build(1, 1, n);
      for (i = 1; i <= m; i++) {
    	scanf("%lld", &k);
    		
        if (k == 1) {
    	  scanf("%lld%lld%lld", &x, &y, &z);
    	  change_x(1, x, y, z);
        } else if (k == 2) {
    	  scanf("%lld%lld%lld", &x, &y, &z);
    	  change(1, x, y, z);
      	} else if (k == 3) {
    	  scanf("%lld%lld", &x, &y);
    	  printf("%lld
    ", getsum(1, x, y));
        }
    //	if (k == 1) {
    //		scanf("%lld%lld%lld",&x,&y,&z);
    //		change(1,x,y,z);
    //	}else if(k == 2){
    //		scanf("%lld%lld",&x,&y);
    //		printf("%lld
    ",getsum(1,x,y));
    //	}
      }
      return 0;
    } 
    

    指针:

    #include <cstdio>
    using namespace std;
    typedef long long int ll;
    
    const int maxn = 100005;
    int n,q,p;
    
    ll a[maxn];
    struct Node{
      ll tag_a, v, tag_b;
      int l, r;
      Node *ls, *rs;
    	
      Node(const int L, const int R) {
        l = L, r = R;
    	tag_a = 0, tag_b = 1;
    	if (l == r)	{
    	  v = a[l];
    	  ls = rs = NULL;
    	} else {
    	  int mid = (L + R) >> 1;
    	  ls = new Node(L, mid);
    	  rs = new Node(mid + 1, R);
    	  push_up(); 
    	}
      }
      inline void make_tag_1(ll w) {//+
    	(v += (r - l + 1) * w) %= p;
    	(tag_a += w) %= p;
      }
      inline void make_tag_2(ll w) {//*
    	(v *= w) %= p;
    	(tag_a *= w) %= p;
    	(tag_b *= w) %= p;
      }
      inline void push_up() {
    	v = ls->v + rs->v;
      }
      inline void push_down() {
    	if (!tag_a && tag_b == 1) return;
    //	if (tag_a && tag_b != 1) {
    //	  ls->make_tag_1(tag_a);
    //	  rs->make_tag_1(tag_a);
    //	  tag_a = 0;
    //	}
    //	if (!tag_a&&tag_b) {
    //	  ls->make_tag_2(tag_b);
    //	  rs->make_tag_2(tag_b);
    //	  tag_b = 1;
    //	}
    	if (tag_b != 1) {
    	  ls->make_tag_2(tag_b);
    	  rs->make_tag_2(tag_b);
    	  tag_b = 1;
    	}
    	if (tag_a) {
    	  ls->make_tag_1(tag_a);
    	  rs->make_tag_1(tag_a);
    	  tag_a = 0;
    	}
      }
      inline bool InRange(const int L, const int R) { return (L <= l && r <= R); }
      inline bool Outofrange(const int L, const int R) { return (L > r || l > R); 	}
    	
      inline void update_a(const int L, const int R, ll w) {//+
    	if (InRange(L, R)) make_tag_1(w);
    	else if (!Outofrange(L, R)) {
    	  push_down();
    	  ls->update_a(L, R, w);	
    	  rs->update_a(L, R, w);
    	  push_up();
    	}
      }
    
      inline void update_b(const int L,const int R,ll w) {//*
    	if(InRange(L, R)) make_tag_2(w);
    	else if(!Outofrange(L, R)) {
    	  push_down();
    	  ls->update_b(L, R, w);
    	  rs->update_b(L, R, w);
    	  push_up();
    	}
      }
      ll query(const int L, const int R) {
    	if (InRange(L, R)) { return v; }
    	if (Outofrange(L, R)) { return 0; }
    	  push_down();
    	return ls->query(L, R) + rs->query(L, R);
      }	
    };
    
    int main() {
      scanf("%lld%lld%lld", &n, &q, &p);
      for(int i = 1; i <= n; i++)
    	scanf("%d", a + i);
      Node *rot = new Node(1, n);
    	
      for(ll o, x, y, z; q; q--) {
    	scanf("%lld%lld%lld", &o, &x, &y);
    	if (o == 1)	{	
      	  scanf("%lld", &z);
    	  rot->update_b(x, y, z);
    	}
    	if (o == 2) {	
    	  scanf("%lld", &z);
    	  rot->update_a(x, y, z);
    	}
    	if (o == 3)	{
    	  ll m = rot->query(x, y);
    	  printf("%lld
    ", m % p);
    	}
      }
      return 0;
    }
    
  • 相关阅读:
    使用Visual Studio 2010来部署Windows应用程序
    如何显示一个非激活窗体
    构建ASP.NET网站十大必备工具(2)
    在Azure中创建一个“Hello World”应用程序
    轻松搞定VS2010 和旧版本服务器一起使用的问题
    Sql注入与转义
    小数型 Float(M,D),decimal(M,D)
    MySQL SQL语句
    作业综合练习配置+自定义函数设置
    作业综合练习初始化工作
  • 原文地址:https://www.cnblogs.com/Kyriech-Francis/p/Class_Segment-tree.html
Copyright © 2020-2023  润新知