zoukankan      html  css  js  c++  java
  • 树状数组与线段树基础

    树状数组 相对于线段树系数少很多,代码量小很多
    • 快速求前缀和 or 某元素更新

    • 可以解决的问题:单点修改,区间查询 or 区间修改,单点查询(差分)

    • O(logn)的时间复杂度

    • lowbit(x) 二进制下,x最低位1代表的值

    • 编号为x的节点,统计[x - lowbit(x) + 1, x]的信息,父节点为x + lowbit(x);//奇数节点C[I]只保存a[i]

    #define lowbit(i) (i & (-i))
    

    树状数组

    • C数组 数据输入的时候直接修改
    int n,m,k,x,y,c[kN],a[kN];//a c等长 c[i](i为2,4,8...2^t)表示:1~i的和;(i为奇数)表示:a[i] 
    //维护树状数组C,初始状态c,a中的元素均为0 
    //i节点的值 + v,不断修改各级父节点 
    void add(int i, int v){ 
        //a[i]直连c[i] 变换相同,修改c[i]的各级父节点
    	for(int j = i; j <= n; j += lowbit(j)) c[j] += v;
    } 
    //查询区间和[1,i] 
    int query(int i){
    	int ans = 0;
    	for(int j = i; j >= 1; j -= lowbit(j)) ans += c[j];
    	return ans;
    }
    

    线段树 O(logn)

    对数组a[n]:单点修改,单点查询,区间查询;区间修改,单点查询,区间查询;

    • 单点/区间修改、询问,min,max,sum......数组a[4n];时间复杂度均为O(logn)

    • 修改: + x,*x, = x...

    • 统计量、修改量可合并,通过修改量可直接修改统计量 -- 满足区级加法即可

    线段树

    • 完全二叉树,将大区间划分为若干单位区间,单位区间为叶子结点;各节点保存一条线段;

      • 根节点区间:1~N
      • 对于树中非叶子节点[a,b],左子节点[a,(a+b)/2],右子节点[(a+b)/2 + 1, b]; ((a + b) >> 1)
      • 编号:父节点:i,左子节点:2i(i << 1),右子节点:2i + 1(i << 1 | 1)
      • lazy标签:记录当前节点变化,访问某一个节点时lazy下传;多个lazy,优先级高的先下传 ;对于某段区间中的元素进行相同的修改,若一个一个的修改效率过低。通过lazy标签,标记这一段区间的变化,在求和的时候直接通过 元素个数 * lazy值,即可表示当前区间和。
      • 预处理O(n),查询、更新O(logn),空间换时间
      • 查找最值:根节点最值为两子树递归求出的最值中符合要求的那个
      • 区间和:总的区间和为两子树各自递归求出的区间和求和
    const int kN = 1e5 + 5;
    struct node{
    	int sum,lazy = 0;//sum 节点代表的区间和,求最值--修改sum为minn,maxx,修改sum计算方式即可 
    };
    int n,m,t,x,y,k,a[kN];
    node tree[4 * kN];
    //i 当前节点编号,l r当前节点区间 
    //递归建树 
    void build_tree(int i,int l, int r){
    	if(l == r){
    		tree[i].sum = a[l];
    		return;
    	}
    	int mid = l + r >> 1;
    	build_tree(i << 1,l,mid);
    	build_tree(i << 1 | 1,mid + 1,r);
    	tree[i].sum = tree[i << 1].sum + tree[i << 1 | 1].sum;
    } 
    //单点修改 a[x] += y
    void change1(int i, int l, int r){
    	if(l == r){//到达叶子结点 
    		tree[i].sum += y;
    		return;
    	}
    	int mid = l + r >> 1;
    	if(x <= mid) change1(i << 1, l, mid);
    	else change1(i << 1 | 1, mid + 1, r);
    	tree[i].sum = tree[i << 1].sum + tree[i << 1 | 1].sum;
    }
    //lazy标签下传 
    void update(int i,int l, int r){
    	tree[i].sum = tree[i].sum + (r - l + 1) * tree[i].lazy;
    	if(l != r){
    		tree[i << 1].lazy += tree[i].lazy;
    		tree[i << 1 | 1].lazy += tree[i].lazy;	
    	} 
    	tree[i].lazy = 0;
    }
    //区间修改 a[x...y] + k
    void change(int i, int l, int r){
    	if(x <= l && y >= r){//被包含
    		tree[i].lazy += k;
    		return;
    	}
    	if(tree[i].lazy != 0) update(i,l,r);//当前节点lazy标签已使用 -- 下传 
    	int mid = (l + r) >> 1;
    	if(y <= mid) change(i << 1, l, mid);
    	else if(x > mid) change(i << 1 | 1, mid + 1, r);
    	else{
    		change(i << 1, l, mid);
    		change(i << 1 | 1, mid + 1, r);
    	}
    	tree[i].sum = tree[i << 1].sum + (mid - l + 1) * tree[i << 1].lazy + tree[i << 1 | 1].sum + (r - mid) * tree[i].lazy;
    }
    //区间查询 
    int query(int i, int l, int r){//a[x..y]
    	if(x <= l && y >= r){
    		return tree[i].sum;
    	}
    	int ans = 0,mid = l + r >> 1;
    	if(x <= mid) ans = query(i << 1, l, mid);
    	if(mid < y) ans += query(i << 1 | 1, mid + 1, r);
    	return ans;
    }
    

    线段树求最大子段和:

    • 子段和存在的三种情况:在左区间;在右区间;包含左右区间分界点 -- 维护相应子段he即可
  • 相关阅读:
    Linq to OBJECT延时标准查询操作符
    LINQ to XML
    动态Linq(结合反射)
    HDU 1242 dFS 找目标最短路
    HDu1241 DFS搜索
    hdu 1224 最长路
    BOJ 2773 第K个与m互质的数
    ZOJ 2562 反素数
    2016 ccpc 杭州赛区的总结
    bfs UESTC 381 Knight and Rook
  • 原文地址:https://www.cnblogs.com/honey-cat/p/12799492.html
Copyright © 2011-2022 走看看