线段树+懒惰标记

In the game of DotA, Pudge’s meat hook is actually the most horrible thing for most of the heroes. The hook is made up of several consecutive metallic sticks which are of the same length.
Now Pudge wants to do some operations on the hook.
Let us number the consecutive metallic sticks of the hook from 1 to N. For each operation, Pudge can change the consecutive metallic sticks, numbered from X to Y, into cupreous sticks, silver sticks or golden sticks.
The total value of the hook is calculated as the sum of values of N metallic sticks. More precisely, the value for each kind of stick is calculated as follows:

For each cupreous stick, the value is 1.
For each silver stick, the value is 2.
For each golden stick, the value is 3.

Pudge wants to know the total value of the hook after performing the operations.
You may consider the original hook is made up of cupreous sticks.
Input
The input consists of several test cases. The first line of the input is the number of the cases. There are no more than 10 cases.
For each case, the first line contains an integer N, 1<=N<=100,000, which is the number of the sticks of Pudge’s meat hook and the second line contains an integer Q, 0<=Q<=100,000, which is the number of the operations.
Next Q lines, each line contains three integers X, Y, 1<=X<=Y<=N, Z, 1<=Z<=3, which defines an operation: change the sticks numbered from X to Y into the metal kind Z, where Z=1 represents the cupreous kind, Z=2 represents the silver kind and Z=3 represents the golden kind.
Output
For each case, print a number in a line representing the total value of the hook after the operations. Use the format in the example.
Sample Input
1
10
2
1 5 2
5 9 3
Sample Output
Case 1: The total value of the hook is 24.
代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define ll long long
const ll N=1e6+10;
ll tree[N],arr[N],lz[N];
void build(ll node,ll start,ll end)//建树
{
	if(start==end) tree[node]=arr[end];
	else
	{
		ll mid=(start+end)/2;
		ll left_node=2*node;
		ll right_node=2*node+1;
		build(left_node,start,mid);
		build(right_node,mid+1,end);
		tree[node]=tree[left_node]+tree[right_node];
	}
}
void push_down(ll node,ll l)//懒惰标记 
{
	if(lz[node])//判断当前节点是否被标记,如果标记更新它的左孩子和右孩子,最后一定不要忘了取消标记
	{
		ll left_node=2*node;
		ll right_node=2*node+1;
		lz[left_node]=lz[node];
		lz[right_node]=lz[node];
		tree[left_node]=(ll)lz[node]*(l-(l/2));
		tree[right_node]=(ll)lz[node]*(l/2);
		lz[node]=0;
	}
}
void update(ll node,ll start,ll end,ll xx,ll yy,ll val)//把一个区间的数全部修改
{
	if(start>=xx&&end<=yy)
	{
		lz[node]=val;
		tree[node]=(ll)val*(end-start+1);
	}
	else
	{
		push_down(node,end-start+1);//每次都要判断是否被标记
		ll mid=(start+end)/2;
		ll left_node=2*node;
		ll right_node=2*node+1;
		if(xx<=mid) update(left_node,start,mid,xx,yy,val);//搜索左子树
		if(yy>mid) update(right_node,mid+1,end,xx,yy,val);//搜索右子树
		tree[node]=tree[left_node]+tree[right_node];//更新数值
	}
}
ll query(ll node,ll start,ll end,ll L,ll R)
{
	if(L>end||R<start) return 0;//如果当前区间在要求区间之外直接返回0
	else if(start>=L&&end<=R) return tree[node];
	else if(start==end) return tree[node];
	else 
	{
		ll mid=(start+end)/2;
		ll left_node=2*node;
		ll right_node=2*node+1;
		ll sum_left=query(left_node,start,mid,L,R);
		ll sum_right=query(right_node,mid+1,end,L,R);
		return sum_left+sum_right;
	}
}
int main()
{
	int t,n,m,i,j,k=1;
	scanf("%d",&t);
	while(t--)
	{
		memset(tree,0,sizeof(tree));
		memset(lz,0,sizeof(lz));
		scanf("%d",&n);
		for(i=1;i<=n;i++)
		{
			arr[i]=1;
		 } 
		 build(1,1,n);
		 scanf("%d",&m);
		 while(m--)
		 {
		 	int a,b,c;
		 	scanf("%d %d %d",&a,&b,&c);
		 	update(1,1,n,a,b,c);
		 }
		 ll sum=query(1,1,n,1,n);
		 printf("Case %d: The total value of the hook is %lld.\n",k++,sum);
	}
}

懒惰标记:
我们
我们来举一个例子来理解懒惰标记,我们假设当前tree[1]-tree[10]全部是1,进行一个操作把1-5全部变为2,5-1+1可以算出他有多少个子节点我们把所有子节点的数值都变为2,那么我们只要执行(5-1+1)* 2就可以了,更新后tree[2]是不是就变成我们想要的了,接着我们对2进行一下懒惰标记,如果后面的操作会用到它我们再进行更新,用不到就不更新,接着我们进行一个把5-9更新为3,在进行搜索的时候肯定会先搜索到2节点这时懒惰标记就开始起作用了,搜索到2节点后会进入Push_back函数对2节点的左孩子右孩子进行一下更新,这个更新就是把它们所包含区间的数全部改为懒惰标记标记的树,同时对他的左右子树进行标记取消本身标记,这时更新完tree[4]就变成6了tree[5]就变成4了,接着我们会通过懒惰标记更新5节点的左右子树把tree[10]和tree[11]都变为2,最后把tree[11]更新为3就可以了,通过一个递归,tree[2]=11,接着我们更新节点6也就是区间6-8,因为之前并没有进行过标记所以直接更新这个区间就可以了tree[6]=(8-6+1)*3=9,最后把tree[14]=3就可以了,通过递归最后得到tree[3]=13,tree[1]=24。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值