Lazy mark of line segment tree for interval addition + interval modification + HDU-1698 + Horrible Queries LightOJ-1164


Question HDU-1698 to the effect:

Given a row of n copper sticks, their initial value is 1. Now there are q operations, you can change the sticks in the interval [x, y] to copper sticks (value 1), silver sticks (value 2) , Golden stick (value of 3). What is the value of all the sticks obtained at the end?

Ideas:

  • Use the line segment tree to save the total value of each interval.
  • Regarding updating the interval [x,y], we use the lazy array to mark, which represents the value of the subinterval of the current interval node that should be modified (updated only when the subinterval is needed for update and query)
  • Finally, use the query query for each question. If the query interval completely covers the maintenance interval, the interval sum is directly returned; if it is not completely covered, the sub-interval node of the current interval node will be used, so downward update is required pushdown operation.
  • When updating a certain interval, the same reason, if it is completely covered, update directly; otherwise, the sub-interval will be used, and the operation must be performed after pushdown.

The AC code is as follows:

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int maxn = 1e5 + 5;
int lazy[maxn << 2], ms[maxn << 2];
int n, q, x, y, z, t;

void pushup(int id){
	ms[id] = ms[id << 1] + ms[id << 1 | 1];
}

void pushdown(int id, int l, int r){
	if(lazy[id]){
		lazy[id << 1] = lazy[id << 1 | 1] = lazy[id];
		int mid = ( l + r ) >> 1;
		ms[id << 1] = (mid - l + 1) * lazy[id];
		ms[id << 1 | 1] = (r - mid) * lazy[id];
		lazy[id] = 0;
	}
}

void update(int id, int l, int r, int x, int y, int v){
	if(x <= l && r <= y){
		lazy[id] = v;
		ms[id] = (r - l + 1) * v;
		return;
	}
	pushdown(id, l, r);
	int mid = (l + r) >> 1;
	if(x <= mid){
		update(id << 1, l, mid, x, y, v);
	}
	if(y > mid){
		update(id << 1 | 1, mid + 1, r, x, y, v);
	}
	pushup(id);
}

int query(int id, int l, int r, int x, int y){
	if(x <= l && r <= y){
		return ms[id];
	}//如果不包含在内,则需要向下更新 
	pushdown(id, l, r);
	int mid = (l + r) >> 1;
	int ans = 0;
	if(x <= mid){
		ans += query(id << 1, l, mid, x, y);
	}
	if(y > mid){
		ans += query(id << 1 | 1, mid + 1, r, x, y);
	}
	return ans;
}

int main(){
	int T = 1;
	scanf("%d",&t); 
	while(t--){
		memset(lazy, 0, sizeof lazy);
		scanf("%d%d", &n, &q);
		update(1, 1, n, 1, n, 1);//将线段树整个区间置位1,即铜棒 
		for(int i = 1;i <= q;i++){
			scanf("%d%d%d", &x, &y, &z);
			update(1, 1, n, x, y, z);
		}
		printf("Case %d: The total value of the hook is %d.\n",  T++, query(1, 1, n, 1, n));
	}
	
	return 0;
}


Question Horrible Queries LightOJ-1164 to the effect:

Given an array of n length, its initial value is all 0. There are q operations, of which the 1 xyv operation represents adding v to the value between the interval [x, y]; and the 0 xy operation represents the query of the sum in the interval [x, y], and the problem is completed according to input and output.

Ideas:

  • Use a line segment tree to maintain the sum value in the interval;
  • Use the lazy array to mark the value that should be added to the sub-interval of the current interval node. When the value of the sub-interval needs to be used, perform a pushdown operation to update the value of the sub-interval node;
  • Finally, use the query query for each question. If the query interval completely covers the maintenance interval, the interval sum is directly returned; if it is not completely covered, the sub-interval node of the current interval node will be used, so downward update is required pushdown operation.
  • When updating a certain interval, the same reason, if it is completely covered, update directly; otherwise, the sub-interval will be used, and the operation must be performed after pushdown.

The AC code is as follows:

#include<cstdio>
#include<cstring>
#include<algorithm>
typedef long long ll;
using namespace std;
const int maxn = 1e5 + 5;
int lazy[maxn << 2];
ll ms[maxn << 2]; 
//lazy数组用于存储下标为id的区间结点中,左右儿子区间应该增加的数值 。
//lazy的作用是在有需要的时候,更新当前结点的左右儿子节点的区间和值,当不需要更新的时候就不做多余的更新 

void pushup(int id){
	ms[id] = ms[id << 1] + ms[id << 1 | 1];
}

void pushdown(int id, int l, int r){
	//父节点区间应该加上lazy的值那么子区间也应该加上同样的值,此为下方操作 
	if(lazy[id]){
		lazy[id << 1] += lazy[id]; //让左二子更新其子节点应该增加的值 
		lazy[id << 1 | 1] += lazy[id];
		int mid = (l + r) >> 1;
		ms[id << 1] += lazy[id] * (mid - l + 1);//左子树的和递增 
		ms[id << 1 | 1] += lazy[id] * (r - mid);//右子树的和递增 
		lazy[id] = 0;//增加之后清空,下一次id这个结点区间就不再增加这个值了。 
	}
}

ll query(int id, int l, int r, int x, int y){
	if(x <= l && r <= y){
		return ms[id];
	}
	pushdown(id, l, r);
	//所查询的区间不完全覆盖所维护的区间,所以将所维护的区间向下推,使得当前结点的子节点区间按照因该加上的值更新。 
	int  mid = (l + r) >> 1;
	ll ans = 0;
	if(x <= mid){
		ans += query(id << 1, l, mid, x, y);
	}
	if(y > mid){
		ans += query(id << 1 | 1,mid + 1, r, x, y);
	}
	return ans;
} 

void update(int id, int l, int r, int x, int y, int v){
	if(x <= l && r <= y){
		ms[id] += (r - l + 1) * v;
		lazy[id] += v;
	}else{
		pushdown(id , l, r);//说明当前区间不再查询区间之内,因此要递归到下一层区间结点。
		//所以之前存在于lazy数组中还没有更新的值此时需要被更新。
		int mid = (l + r) >> 1;
		if(x <= mid){
			update(id << 1, l, mid, x, y, v);
		} 
		if(y > mid){
			update(id << 1 | 1, mid + 1, r, x, y, v);
		}
		pushup(id);//向上合并。 
	}
} 

int t, n, m, op, x, y, d;
int main() {
	int cnt = 1;
	scanf("%d", &t);
	while (t--) {
		memset(lazy, 0, sizeof(lazy));
		memset(ms, 0, sizeof(ms));
		printf("Case %d:\n", cnt++);
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= m; i++) {
			scanf("%d%d%d", &op, &x, &y);
			x++, y++;
			if (op == 0) {
				scanf("%d", &d); 
				update(1, 1, n, x, y, d);
			} else {
				printf("%lld\n", query(1, 1, n, x, y));
			}
		}
	} 
	return 0;
} 

 

Guess you like

Origin blog.csdn.net/qq_40596572/article/details/104014303