线段树--lazy标记

例题

「POJ3468」A Simple Problem with Integers
时间限制: 1 Sec 内存限制: 128 MB
题目描述
你有一些整数,和一些操作与询问需要处理
输入
第一行:N,Q,1 ≤ N,Q ≤ 100000.

第二行:N个整数, A1, A2, … , AN. -1000000000 ≤ Ai ≤ 1000000000

接下来:Q行,表示操作与询问

C a b c 表示,区间[a,b]全部增加c,-10000 ≤ c ≤ 10000.

Q a b 表示求区间[a,b]的和

输出
每个询问一个结果,一行。
样例输入
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
样例输出
4
55
9
15

分析

显然是用线段树,但是是修改区间,如果用循环会超时,所以我们用线段树+lazy标记。
还有,注意数据范围,要用long long(我也被卡了一次)。

代码

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define full(a,b,c) fill(b,b+sizeof a/4,c)
#define N 100000+5
int n,m,arr[N];
long long s[4*N],lazy[4*N];//注意数据! 
void built(int k,int l,int r)//建树 
{
	if(l==r)
	{
		s[k]=1LL*arr[l];
		return;
	}
	int mid=(l+r)>>1;
	built(k*2,l,mid);
	built(k*2+1,mid+1,r);
	s[k]=s[k*2]+s[k*2+1];
}
void pushdown(int k,int l,int r)//把k的懒传递给儿子 
{
	if(!lazy[k])return;//不懒 
	int mid=(l+r)>>1;
	s[k*2]+=(mid-l+1)*lazy[k];//左儿子 
	lazy[k*2]+=lazy[k];
	s[2*k+1]+=(r-mid)*lazy[k];//右儿子 
	lazy[k*2+1]+=lazy[k];
	lazy[k]=0;//传递后就不懒了 
}
void update(int k,int l,int r,int x,int y,int v)//把[x~y]+v 
{
	if(x>r||y<l)return;
	if(x<=l&&r<=y)//包含 
	{
		s[k]+=1LL*(r-l+1)*v;//有(r-l+1)个数 
		lazy[k]+=1LL*v;//标记我懒了 
		return;
	}//不包含 
	int mid=(l+r)>>1;
	pushdown(k,l,r);//先传递父亲的懒 
	update(k*2,l,mid,x,y,v);
	update(k*2+1,mid+1,r,x,y,v);
	s[k]=s[k*2]+s[k*2+1];
}
long long ask(int k,int l,int r,int x,int y)//询问[x~y]的和 
{
	if(x>r||y<l)return 0;
	if(x<=l&&r<=y)return s[k];
	int mid=(l+r)>>1;
	pushdown(k,l,r);//先传递父亲的懒
	return ask(k*2,l,mid,x,y)+ask(k*2+1,mid+1,r,x,y);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1; i<=n; i++)
		scanf("%d",&arr[i]);
	built(1,1,n);
	for(int i=1; i<=m; i++)
	{
		char flag;
		scanf("\n%c",&flag);
//		printf("%c*\n",flag);
		if(flag=='C')
		{
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);
			update(1,1,n,a,b,c);
		}
		if(flag=='Q')
		{
			int a,b;
			scanf("%d%d",&a,&b);
			printf("%lld\n",ask(1,1,n,a,b));
		}
	}
}

虽然可以懒标记,但是打代码别懒。(其实我也想懒

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值