ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

树状数组d

2022-02-23 19:32:08  阅读:140  来源: 互联网

标签:树状 int ll mid grid 数组 push sum


stl+树状数组完美解

​​​​​​  思路:

如果想改一个值:最朴素的想法是先把原数组的那个值减去,再将新的值加上。这样会tle

正确的想法应该是直接 update(新值 - 旧值)

 代码实现

string(grid[])本身可以看做一维char数组

vector的assign用法,用vector定义二维数组

#include<bits/stdc++.h>
using namespace std;
const int N = 505;
std::vector<vector<int> > bit;
int m,n,q,h,i,j,k,op;
string s;
struct 
{
	int lb(int x){return x & -x;}
	void update(int x,int y, int v){
		for(; x <= n; x += lb(x))
			for(int p = y; p <= n; p += lb(p))
				bit[x][p] += v;
	}
	ll pre(int x, int y){
		ll ans = 0;
		for(; x; x -= lb(x))
			for(int p = y; p; p -= lb(p))
				ans += bit[x][p];
		return ans;
	}
	ll rpre(int z,int x,int c,int v){
		return pre(c,v) - pre(z -1,v) - pre(c,x - 1) + pre(z-1,x-1);
	}
}bt;


int main(){
	cin>>m;
	while(m--)
	{
		cin>>n>>q;
		bit.assign(N,vector<int>(N,0));
		std::vector<string> grid(n);
		rep(i,0,n){
			cin>>grid[i];
			rep(j,0,n) 
			bt.update(i + 1,j + 1,grid[i][j] - 'A' + 1);
		}
		while(q--){
			cin>>op;
			if(op == 1){
				cin>>h>>i>>j>>k;
				cout<<bt.rpre(h+1,i+1,j+1,k+1)<<endl;
			}else{
				cin>>i>>j>>s;
				if(i == 0){
					rep(k,0,n){
						bt.update(j + 1,k + 1,s[k] - grid[j][k]);
						grid[j][k] = s[k];
					}
				}else{
					rep(k,0,n){
						bt.update(k + 1,j + 1,s[k] - grid[k][j]);
						grid[k][j] = s[k];
					}
				}
			}
		}

	}
	return 0;
}

线段树

段树就是分块思想的树化,或者说是对于信息处理的二进制化——用于达到O(logn)O(logn)级别的处理速度

分块的思想是通过将整个序列分为有穷个小块,对于要查询的一段区间,总是可以整合成kk个所分块与mm个单个元素的信息

通过将整个序列分为有穷个小块,对于要查询的一段区间,总是可以整合成kk个所分块与mm个单个元素的信息的并(0<=k,m<=\sqrt{n})(0<=k,m<=n​)。但普通的分块不能高效率地解决很多问题,所以作为loglog级别的数据结构,线段树应运而生。

二进制位左移一位代表着数值*2∗2,而如果左移完之后再或上11,由于左移完之后最后一位二进制位上一定会是00,所以|1∣1等价于+1+1。

push uppushup操作的目的是为了维护父子节

 void push_up_sum(int p){
		t[p]=t[lc(p)]+t[rc(p)];
    }//	向上不断维护区间操作 
	
	void push_up_min(int p){//max and min
	 t[p]=min(t[lc(p)],t[rc(p)]);
     //t[p]=max(t[lc(p)],t[rc(p)]);             
    }

点之间的逻辑关系。当我们递归建树时,对于每一个节点我们都需要遍历一遍,并且电脑中的递归实际意义是先向底层递归,然后从底层向上回溯,所以开始递归之后必然是先去整合子节点的信息,再向它们的祖先回溯整合之后的信息

完美版本:

下标都是由1~n
ll arr[N];
struct segmenttree
{
	ll info[N<<2],tag[N<<2];
	inline ll ls(ll x){return x<<1;}//leftson
	inline ll rs(ll x){return x<<1|1;}//rightson
	
	void push_up_sum(int p){
		info[p] = info[ls(p)] + info[rs(p)];
	}//线段树的维护对象,回溯的时候回溯的对象

	void build(ll p, ll l, ll r){
		tag[p] = 0;
		if(l == r){info[p] = arr[l];return;}
		ll mid = (l + r) >> 1;
		build(ls(p), l, mid);//向下继续建树
		build(rs(p), mid + 1, r);
		//建完之后该向上回溯了
		push_up_sum(p);
	}

	inline void down(ll p, ll l ,ll r ,ll k)
	{
		tag[p] += k;
		info[p] += k * (r - l + 1);
	}

	inline void push_down(ll p, ll l, ll r)
	{
		ll mid = (l + r)>>1;
		down(ls(p), l, mid, tag[p]);
		down(rs(p), mid + 1, r, tag[p]);
		tag[p] = 0;
	}

	inline void update(ll p, ll nl, ll nr, ll l, ll r, ll k)//nl,nr目标区间
	{
		if(nl <= l and r <= nr){
			tag[p] += k;
			info[p] += k * (r - l + 1);
			return;
		}//和上面一样
		push_down(p, l, r);
		ll mid = (l + r)>>1;
		if(nl <= mid)update(ls(p), nl, nr, l, mid, k);
		if(nr > mid) update(rs(p), nl, nr, mid + 1, r, k);
		push_up_sum(p);
		//info[p] = info[ls(p)] + info[rs(p)];
		//因为是=号所以就算第二次push_down了下面改了上面也不变
	}
	
	inline ll query(ll p, ll q_l, ll q_r, ll l, ll r)	
	{
		if(r < q_l or l > q_r)return 0;
		if(q_l <= l and r <= q_r)return info[p];
		int mid = (l + r)>>1;
		push_down(p, l, r);
		ll sum_l = query(ls(p), q_l, q_r, l, mid);
		ll sum_r = query(rs(p), q_l, q_r, mid + 1, r);
		return sum_r + sum_l;
	}
	
}st;

标签:树状,int,ll,mid,grid,数组,push,sum
来源: https://blog.csdn.net/IsayIwant/article/details/121706931

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有