线段树 CSU2151

2151: 集训难度

Submit Page    Summary    Time Limit: 1 Sec     Memory Limit: 512 Mb     Submitted: 124     Solved: 34    


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

 

线段树模板题,特别的地方是同时有更新与累加操作,那么就需要两个tag,而我这里转换了一下,用tag表示累加,re表示更新值。

另外关注一下changetag的整体写法,推荐先维护好本结点,tag与re标记的是要更新下一节点这样子的状态,changetag函数放在后面也在时间空间更优叭。

 

代码:

#include <cstdio>
#include <cstring>

const int maxn=1e5+20;

int n,m,v;

struct node{
	int l,r;
	int re;
	long long sum,tag;
};
node tree[maxn<<2];

void build(int k,int l,int r)
{
	tree[k].l=l;tree[k].r=r;
	tree[k].sum=1ll*(r-l+1)*v;	//一开始写反了
	tree[k].tag=0;tree[k].re=0;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
}

void changetag(int k)
{
	int mid=(tree[k].l+tree[k].r)>>1;
	if(tree[k].re){
		tree[k].re=0;
		tree[k<<1].re=tree[k<<1|1].re=1;
		tree[k<<1].tag=tree[k<<1|1].tag=0;
		tree[k<<1].sum=tree[k<<1|1].sum=0;
	}
	tree[k<<1].tag+=tree[k].tag;
	tree[k<<1|1].tag+=tree[k].tag;
	tree[k<<1].sum+=1ll*(mid-tree[k].l+1)*tree[k].tag;  //两个int相乘有可能溢出,1ll*可防止
	tree[k<<1|1].sum+=1ll*(tree[k].r-mid)*tree[k].tag;
	tree[k].tag=0;
}

void add(int k,int l,int r,int x)
{
	if(tree[k].l==l&&tree[k].r==r){
		tree[k].tag+=x;
		tree[k].sum+=1ll*(r-l+1)*x;
		return;
	}
	if(tree[k].tag||tree[k].re)     //写在这里省空间,表示的是传递给下一层的,本结点已经维护好了
		changetag(k);
	int mid=(tree[k].l+tree[k].r)>>1;
	if(r<=mid)
		add(k<<1,l,r,x);
	else if(l>=mid+1)
		add(k<<1|1,l,r,x);
	else{
		add(k<<1,l,mid,x);
		add(k<<1|1,mid+1,r,x);
	}
	tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}

void update(int k,int l,int r,int x)
{
	if(tree[k].l==l&&tree[k].r==r){
		tree[k].re=1;
		tree[k].tag=x;
		tree[k].sum=1ll*(r-l+1)*x;
		return;
	}
	if(tree[k].tag||tree[k].re)
		changetag(k);
	int mid=(tree[k].l+tree[k].r)>>1;
	if(r<=mid)
		update(k<<1,l,r,x);
	else if(l>=mid+1)
		update(k<<1|1,l,r,x);
	else{
		update(k<<1,l,mid,x);
		update(k<<1|1,mid+1,r,x);
	}
	tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}

long long query(int k,int l,int r)
{
	if(tree[k].l==l&&tree[k].r==r){
		return tree[k].sum;
	}
	if(tree[k].tag||tree[k].re)
		changetag(k);
	int mid=(tree[k].l+tree[k].r)>>1;
	if(r<=mid)
		return query(k<<1,l,r);
	else if(l>=mid+1)
		return query(k<<1|1,l,r);
	else
		return query(k<<1,l,mid)+query(k<<1|1,mid+1,r);
}

int main()
{
	int com,x,y;

	while(~scanf("%d%d%d",&n,&m,&v)){
		memset(tree,0,sizeof(tree));
		build(1,1,n);
		for(int i=0;i<m;++i){
			scanf("%d",&com);
			switch(com){
			case 0:
				scanf("%d%d%d",&x,&y,&v);
				add(1,x,y,v);
				break;
			case 1:
				scanf("%d%d%d",&x,&y,&v);
				update(1,x,y,v);
				break;
			case 2:
				scanf("%d%d",&x,&y);
				printf("%lld\n",query(1,x,y));
				break;
			default: break;
			}
		}
	}

	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值