• IndexTree以及应用



    IndexTree

    IndexTree可以以O(logN)的时间得到任意前缀和,并同时支持在O(logN)时间内支持动态单点值的修改。空间复杂度O(N)。
    相比于线段树对一段区间进行修改,IndexTree适合对数组的单点进行修改。因为线段树如果进行单点修改,那么它大量的非叶子节点都要进行修改。

    IndexTree的前缀和数组

    不同于一般的前缀和数组,IndexTree前缀和数组0位置抛弃不用。
    IndexTree前缀和数组第 i 位置,将i转化成二进制,将最后边的1削掉,然后加1,假设得到的数是x,原数组中x~i的和就是第 i 位置所填的值。

    假设 i 为8,它的二进制序列为1000,我们将它最右边的1消掉再将这个数加1:也就是0000+1=0001,他前缀和的范围就是消去最右边1的数+1到他本身也就是1~8,也就是说原数组第1个数到第8个数的累加和放到IndexTree的前缀和数组第8个位置。

    比如求1~33的累加和,33的二进制为100001,那它的累加和就是它本身加上累加和数组中第32(100000)个位置,因为第32个位置的前缀和就是原数组中0~100000位置的和。

    代码实现

    DP34 【模板】前缀和

    #include<vector>
    #include<queue>
    #include<string>
    #include<iostream>
    using namespace std;
    
    class IndexTree {
    public:
    	//下标从1开始
    	IndexTree(int size)
    		:tree(size + 1)
    		, n(size)
    	{}
    	//从1~index的累加和
    	long long sum(int index) 
    	{
    
    		long long ret = 0;
    		while (index > 0) 
    		{
    			//index&-index:提取index最右侧的1来
    			// 
    			//比如我们要求1~12位置的累加和
    			//12的二进制位1100,先加上tree[1100]
    			//因为tree[1100]的位置是1001~1100的累加和
    			//再将最右端的1去掉,加上tree[1000]
    			//tree[1000]的位置是1~1000的累加和
    			ret += tree[index];
    			index -= (index & -index);
    		}
    		return ret;
    	}
    	void add(int index, int d) 
    	{
    		//index&-index:提取index最右侧的1来
    		//给index位置加上d,那么其他位置也会受到影响
    		// 
    		//比如给1位置加上d,
    		// 2位置是1~2的和,8位置是1~8的和,4位置是1~4的和
    		//这些位置都得加上d
    		while (index <= n) 
    		{
    			tree[index] += d;
    			index += (index & -index);
    		}
    	}
    	//求从原数组中index1~index2的和
    	long long RangeSum(int index1, int index2) 
    	{
    		return sum(index2) - sum(index1);
    	}
    private:
    	vector<long long>tree;
    	int n;
    };
    int main()
    {
        int n=0,q=0;
        cin>>n>>q;
        vector<long long>v(n);
        IndexTree indexTree(n);
        for(int i=0;i<n;i++)
        {
            cin>>v[i];
            indexTree.add(i+1,v[i]);
        }
        while(q--)
        {
            int index1,index2;
            cin>>index1>>index2;
            cout<<indexTree.RangeSum(index1-1,index2)<<endl;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    二维IndexTree

    indexTree相比线段树的优点就是indexTree改成二维的非常的容易的改。indexTree在二维中同样的第一行和第一列的位置是空出来不使用的,这与一维的类似,而在二维中求累加和和一维的原理一样,只不过是多了列。更新也是一样的原理只不过比一维的多了列。
    817 · 范围矩阵元素和-可变的

    class NumMatrix {
    public:
        vector<vector<int>>tree;
        vector<vector<int>>nums;
        int N;
        int M;
        NumMatrix(vector<vector<int>> matrix) 
        {
            if(matrix.size()==0||matrix[0].size()==0)
            {
                return;
            }
            N=matrix.size();
            M=matrix[0].size();
            tree.resize(N+1,vector<int>(M+1));
            nums.resize(N,vector<int>(M));
            for(int i=0;i<N;i++)
            {
                for(int j=0;j<M;j++)
                {
                    update(i,j,matrix[i][j]);
                }
            }
        }
        //从(1,1)到(row,col)的累加和
        int sum(int row,int col)
        {
            int res=0;
            for(int i=row+1;i>0;i-=i&(-i))
            {
                for(int j=col+1;j>0;j-=j&(-j))
                {
                    res+=tree[i][j];
                }
            }
            return res;
        }
        void update(int row, int col, int val) {
            if(N==0||M==0)
            {
                return;
            }
            int add=val-nums[row][col];
            nums[row][col]=val;
            for(int i=row+1;i<=N;i+=i&(-i))
            {
                for(int j=col+1;j<=M;j+=j&(-j))
                {
                    tree[i][j]+=add;
                }
            }
        }
        
        int sumRegion(int row1, int col1, int row2, int col2) 
        {
            if(N==0||M==0)
            {
                return 0;
            }
            return sum(row2,col2)+sum(row1-1,col1-1)-sum(row1-1,col2)-sum(row2,col1-1);
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
  • 相关阅读:
    【数据结构】排序之插入排序和选择排序
    AT2401C 功率放大器(PA)射频前端集成芯片
    高云FPGA系列教程(4):片上逻辑分析仪GAO的使用
    Java:MessageDigest&Base64类的解析与使用
    Stream.toList()和Collectors.toList()的性能比较
    微信小程序使用echarts组件实现饼状统计图功能
    什么是微波通信?微波信号源如何去选择?------TFN TG 115
    【大厂招聘试题】__嵌入式开发工程师_2023届“联想”_3
    【计算机网络】Servlet API重点知识汇总
    电脑进水无法开机怎么办 电脑进水开不了机的解决方法
  • 原文地址:https://blog.csdn.net/qq_52670477/article/details/125469103