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即可
  • 相关阅读:
    PE格式详细讲解2 系统篇02|解密系列
    结构体与共用体06 零基础入门学习C语言58
    PE格式详细讲解3 系统篇03|解密系列
    我的学习路
    结构体与共用体07 零基础入门学习C语言59
    PE格式详细讲解2 系统篇02|解密系列
    PE格式详细讲解3 系统篇03|解密系列
    Windows Azure 社区新闻综述(#61 版)
    Django 现可在 Windows Azure 上使用
    Windows Azure Active Directory正式发布:已提供了超过 2650 亿个身份验证和服务了290万个组织!
  • 原文地址:https://www.cnblogs.com/honey-cat/p/12799492.html
Copyright © 2011-2022 走看看