c++:基本复习和常见错误
查看原帖
c++:基本复习和常见错误
736145
Canye__楼主2024/5/16 20:35

做了N遍,总算过了,太菜了写一下巩固一下


前提

首先根据题目n的最大为100000,那么根据,,反正定义数组为maxn * 4绝对不会错。

因为数据中“保证任意时刻数列中所有元素的绝对值之和 <= 1e18”所以可以用long long定义。 f 为线段树数组,z 为标记

int n, m, a[100010];
//	线段树		懒标记 
ll f[400040], z[400040];
建树

首先要明白树的下标怎么算的, 树的根节点一般为 1 格式: 1 2 3 4 5 6 7 .... 因为我们可以发现,从1开始 i2和i2+1为其子节点 而对于任意一个节点 q 其父节点为 q/2(或q>>1)

inline void buildTree(int k,int l,int r){
	if(l==r){		//当走到根节点时更新 f 值,并返回不在向下走。
		f[k] = a[l];
		return;
	}
	//取 l  r 中点
	int m = (l+r) / 2;
	//递归调用
	buildTree(k + k, l, m);
	buildTree(k + k + 1, m + 1, r);
	//回溯时从下到上计算f的值
	f[k] = f[k + k] + f[k + k + 1];
}
添加

将所需加的区间的 z 进行添加,并在访问时,将访问上有值的 z数组 的值更新的节点的两个子节点中,最后在回溯时计算节点值 节点值:f[k] = f[k+k] + (z[k+k] * (m-l+1)) + f[k+k+1] + (z[k+k+1] * (r-m)); ::需要注意添加的 k 的值可能为报 int ,需要进行修改

//区间加 
inline void addTree(int k, int l, int r, int x, int y, ll v){
	if(l==x && r==y){
		//当访问至当前区间时,打上懒标记
		z[k] += v;
		return;
	}
	//如果存在懒标记,且未到所需查询区间的最底层,更新懒标记下放 
	if(z[k])
		z[k+k] += z[k], z[k+k+1] += z[k], z[k] = 0;
	
	int m = (l+r) >> 1;
  //需要注意区间的划分,因为左边时l到m,所以 y<=m 的时候才走左边;右边的区间为 m+1 到 r,即x>m(或x>=m+1)
	if(y <= m)	//所需添加的区间在以m为中点的左边 
		addTree(k+k, l, m, x, y, v);
	else if(x > m)	//在右边 
		addTree(k+k+1, m+1, r, x, y, v);
	else		//都有 
		addTree(k+k, l, m, x, m, v), addTree(k+k+1, m+1, r, m+1, y, v);
	//回溯时更新线段树
	f[k] = f[k+k] + (z[k+k] * (m-l+1)) + f[k+k+1] + (z[k+k+1] * (r-m));
}
求和

求和总体思路与添加一致,不过退出时不需要修改 z 的值,只需要在访问区间时进行修改非0 的 z。并且是有返回值的 dfs ,返回值存储在 ans 在回溯后返回。

inline ll getTree(int k, int l, int r,int x,int y){
	if(l==x && r==y)
		return f[k] + (z[k] * (r-l+1));
	//更新懒标记
	if(z[k])
		z[k+k] += z[k], z[k+k+1] += z[k], z[k] = 0;
		
	int m = (l+r) >> 1;
	ll ans = 0;
	
	if(y <= m)	//在左边
		ans += getTree(k+k, l, m, x, y);
	else if(x > m)	//在右边
		ans += getTree(k+k+1, m+1, r, x, y);
	else
		ans += getTree(k+k, l, m, x, m) + getTree(k+k+1, m+1, r, m+1, y); 
		
	//回溯时更新线段树 
	f[k] = f[k+k] + (z[k+k] * (m-l+1)) + f[k+k+1] + (z[k+k+1] * (r-m));
	
	return ans;
} 
最后上ac代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;


int n, m, a[100010];
//	线段树		懒标记 
ll f[400040], z[400040];

//建树 
inline void buildTree(int k,int l,int r){
	if(l==r){
		f[k] = a[l];
		return;
	}
	
	int m = (l+r) / 2;
	
	buildTree(k + k, l, m);
	buildTree(k + k + 1, m + 1, r);
	
	f[k] = f[k + k] + f[k + k + 1];
}
//区间加 
inline void addTree(int k, int l, int r, int x, int y, ll v){
	if(l==x && r==y){
		//打上懒标记
		z[k] += v;
		return;
	}
	//更新懒标记下放 
	if(z[k])
		z[k+k] += z[k], z[k+k+1] += z[k], z[k] = 0;
	
	
	int m = (l+r) >> 1;
	if(y <= m)	//在左边 
		addTree(k+k, l, m, x, y, v);
	else if(x > m)	//在右边 
		addTree(k+k+1, m+1, r, x, y, v);
	else		//都有 
		addTree(k+k, l, m, x, m, v), addTree(k+k+1, m+1, r, m+1, y, v);
	//回溯时更新线段树
	f[k] = f[k+k] + (z[k+k] * (m-l+1)) + f[k+k+1] + (z[k+k+1] * (r-m));
}

//区间求和
inline ll getTree(int k, int l, int r,int x,int y){
	if(l==x && r==y)
		return f[k] + (z[k] * (r-l+1));
	//更新懒标记
	if(z[k])
		z[k+k] += z[k], z[k+k+1] += z[k], z[k] = 0;
		
	int m = (l+r) >> 1;
	ll ans = 0;
	
	if(y <= m)	//在左边
		ans += getTree(k+k, l, m, x, y);
	else if(x > m)	//在右边
		ans += getTree(k+k+1, m+1, r, x, y);
	else
		ans += getTree(k+k, l, m, x, m) + getTree(k+k+1, m+1, r, m+1, y); 
		
	//回溯时更新线段树 
	f[k] = f[k+k] + (z[k+k] * (m-l+1)) + f[k+k+1] + (z[k+k+1] * (r-m));
	
	return ans;
} 


int main(){
	
	scanf("%d%d",&n,&m);
	for(int i=1; i<=n; i++)
		scanf("%d",&a[i]);
	//建树 
	buildTree(1,1,n);
	
	for( ;m--; ){
		int temp,x,y;
		scanf("%d%d%d",&temp,&x,&y);
		if(temp&1){		//区间加 
			ll k;
			scanf("%lld",&k);
			addTree(1, 1, n, x, y, k);
		}else{			//输出和 
			printf("%lld\n",getTree(1,1,n,x,y));
		}
	}
	
	
	return 0;
}
2024/5/16 20:35
加载中...