CSU 2151 集训难度 线段树

http://acm.csu.edu.cn:20080/csuoj/problemset/problem?pid=2151

Description

小L正在组织acm暑假集训,但众所周知,暑假集训的萌新中有OI神犇,也有暑假才开始学算法的萌新,如果统一集训的难度,无法很好地让萌新们得到训练,所以小L想了一个办法,根据每次测试的情况,改变萌新们的集训难度。现在将萌新们编号为1到n,最初萌新们的集训难度为v0,测试后有两种操作,第一种是某一区间的萌新的集训难度同时提高,另一种是将某一段区间的萌新的集训难度变为同一个数,同时,Wells希望在某次调整难度之后,知道某一段区间的萌新的集训难度之和,由于小L比较鶸,他并不知道如何快速解决这个问题,你能帮帮他嘛?

Input

第一行三个数n,m,v0 表示有n名萌新和m次调整,初始时全部萌新的集训难度都为v0

第2~m+1行 每行三个数或四个数

0 x y v 表示把 [x,y]区间内的萌新的集训难度都增加v

1 x y v 表示把 [x,y]区间内的萌新的集训难度都变为v

2 x y表示询问[x,y]区间内萌新的集训难度之和

0<n,m<=10^5, |v|<=10^5

Output

每个询问一行,输出答案

Sample Input

3 5 0
0 1 3 1
1 2 3 2
2 1 1  
2 2 2
2 2 3

Sample Output

1
2
4

Hint

Source

某场noip模拟题

Author

lfw

思路:基本上是裸的线段树题目吧,涉及到区间修改和区间覆盖,因此我们用两个lazy标记就好了。

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;

struct node
{
	int l,r;
	ll sum,lazy1,lazy2;
};
node tree[400005];
int n,m;
ll v0;

void build(int k,int l,int r)
{
	tree[k].l=l,tree[k].r=r;
	tree[k].lazy2=INF;
	if(l==r)
	{
		tree[k].sum=v0;
		return ;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}

void down1(int i)//区间修改
{
	tree[i<<1].lazy1+=tree[i].lazy1;
	tree[i<<1|1].lazy1+=tree[i].lazy1;
	tree[i<<1].sum+=tree[i].lazy1*(tree[i<<1].r-tree[i<<1].l+1);
	tree[i<<1|1].sum+=tree[i].lazy1*(tree[i<<1|1].r-tree[i<<1|1].l+1);
	tree[i].lazy1=0;
}

void down2(int i)//区间覆盖
{
	tree[i<<1].lazy2=tree[i].lazy2;
	tree[i<<1|1].lazy2=tree[i].lazy2;
	tree[i<<1].lazy1=tree[i<<1|1].lazy1=0;
	tree[i<<1].sum=tree[i].lazy2*(tree[i<<1].r-tree[i<<1].l+1);
	tree[i<<1|1].sum=tree[i].lazy2*(tree[i<<1|1].r-tree[i<<1|1].l+1);
	tree[i].lazy2=INF;
}

void update(int p,int i,int l,int r,ll v)
{
	if(tree[i].l==l&&tree[i].r==r)
	{
		if(p==0)//区间修改
		{
			tree[i].lazy1+=v;
			tree[i].sum+=(r-l+1)*v;
		}
		else if(p==1)//区间覆盖
		{
			tree[i].lazy2=v;
			tree[i].lazy1=0;//很重要
			tree[i].sum=(r-l+1)*v;
		}
		return ;
	}
	if(tree[i].lazy2!=INF)
		down2(i);
	if(tree[i].lazy1)
		down1(i);
	int mid=(tree[i].l+tree[i].r)>>1;
	if(r<=mid)
		update(p,i<<1,l,r,v);
	else if(l>=mid+1)
		update(p,i<<1|1,l,r,v);
	else
	{
		update(p,i<<1,l,mid,v);
		update(p,i<<1|1,mid+1,r,v);
	}
	tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;
}

ll query(int i,int l,int r)
{
	if(tree[i].l==l&&tree[i].r==r)
		return tree[i].sum;
	if(tree[i].lazy2!=INF)
		down2(i);
	if(tree[i].lazy1)
		down1(i);
	int mid=(tree[i].l+tree[i].r)>>1;
	if(r<=mid)
		return query(i<<1,l,r);
	else if(l>=mid+1)
		return query(i<<1|1,l,r);
	else
		return query(i<<1,l,mid)+query(i<<1|1,mid+1,r);
}

int main()
{
	scanf("%d %d %lld",&n,&m,&v0);
	build(1,1,n);
	int p,l,r;
	ll v;
	while(m--)
	{
		scanf("%d %d %d",&p,&l,&r);
		if(p==0||p==1)
		{
			scanf("%lld",&v);
			update(p,1,l,r,v);
		}
		else if(p==2)
			printf("%lld\n",query(1,l,r));
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值