厦门大学程序设计大赛月赛 D(线段树:区间加&&区间乘)

l i n k link link

题意:

给你一个数组,有5种操作,如下:

  • 操作 1: 格式:1 l l l r r r k k k ;含义:将区间 [ l , r ] [l,r] [l,r] 内每个英雄的攻击力加上 k k k
  • 操作 2: 格式:2 l l l r r r k k k ; 含义:将区间 [ l , r ] [l,r] [l,r] 内每个英雄的攻击力乘上 k k k
  • 操作 3: 格式:3 l l l r r r k k k ;含义:将区间 [ l , r ] [l,r] [l,r] 内每个英雄的攻击力置为 k k k
  • 操作4:格式:4 x x x ;含义:在最后一个英雄后增加一个攻击力为 x x x 的英雄
  • 操作5: 格式:5 l l l r r r k k k ;含义:输出区间 [ l , r ] [l,r] [l,r] 内每个英雄的攻击力的和并对 p p p取模

思路:

单独的1操作,单独的2操作就是线段树基本操作,主要是1,2操作混合起来有点难,怎么打懒标记?我们设置一个add代表加法的懒标记,mul代表乘法的懒标记。

先考虑对某个节点加懒标记:
  • 为这个节点加add标记,直接加即可;加乘法标记,如果之间这个节点有add标记,那么之前的add标记也要乘以这个值,因为 ( a + b ) ∗ v a l = a ∗ v a l + b ∗ v a l (a+b)*val = a*val + b*val a+bval=aval+bval;
再考虑如何下传:
  • 因为节点下传标记之前,节点的值已经更新了,就考虑下传问题,首先add来说,下传后的左右儿子的add应该等于: a d d [ s o n ] ∗ m u l [ f a t h e r ] + a d d [ f a t h e r ] add[son]*mul[father] + add[father] add[son]mul[father]+add[father];
  • 对于mul来说,下传就直接乘就可以了。

感觉三言两语讲不清,还是结合代码看看吧。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 2e5+100;

ll tr[maxn * 4],add[maxn * 4],mul[maxn * 4],mod;

#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define ls o<<1
#define rs o<<1|1
#define mid (l+r)/2
void pushup(int o)
{
	tr[o] = (tr[ls] + tr[rs])%mod;
}
void down(int o,int l,int r)
{
	if(mul[o] == 1 && add[o] == 0)return ;
	tr[ls] = (tr[ls]*mul[o]%mod + add[o]*(ll)(mid-l+1)%mod) % mod;
	tr[rs] = (tr[rs]*mul[o]%mod + add[o]*(ll)(r-mid)%mod) % mod;
	add[ls] = (add[ls]*mul[o]%mod + add[o])%mod;
	add[rs] = (add[rs]*mul[o]%mod + add[o])%mod;
	mul[ls] = (mul[ls]*mul[o])%mod;
	mul[rs] = (mul[rs]*mul[o])%mod;
	mul[o] = 1,add[o] = 0;
}
void up(int o,int l,int r,int p,ll val)
{
	if(l == r)
	{
		tr[o] = val%mod;
		return ;
	}
	down(o,l,r); 
	if(p <= mid)up(lson,p,val);
	else up(rson,p,val);
	pushup(o);
}

void up_add(int o,int l,int r,int L,int R,int val)
{
	if(l >= L && r <= R)
	{
		tr[o] = (tr[o] + 1ll*(r-l+1)*val%mod) % mod;
		add[o] = (add[o] + val)%mod;
		return ;
	}
	down(o,l,r);
	if(L <= mid)up_add(lson,L,R,val);
	if(R > mid)up_add(rson,L,R,val);
	pushup(o);
}

void up_mul(int o,int l,int r,int L,int R,int val)
{
	if(l >= L && r <= R)
	{
		tr[o] = (tr[o]*(ll)val)%mod;
		add[o] = (add[o]*(ll)val)%mod;
		mul[o] = (mul[o]*(ll)val)%mod;
		return ;
	}
	down(o,l,r);
	if(L <= mid)up_mul(lson,L,R,val);
	if(R > mid)up_mul(rson,L,R,val);
	pushup(o);
}

ll qu(int o,int l,int r,int L,int R)
{
	if(l >= L && r <= R)return tr[o]%mod;
	down(o,l,r);
	ll ans = 0;
	if(L <= mid)ans = (ans + qu(lson,L,R)%mod) % mod;
	if(R > mid)ans = (ans + qu(rson,L,R)%mod) % mod;
	return ans;
}
int main()
{
	int n,m,N;
	ll x;
	scanf("%d%d%d",&n,&m,&mod);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&x);
		up(1,1,n+m+5,i,x);
	}
	N = n;
	n = n + m + 5;
	int op,l,r,k;
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&op);
		if(op == 1)
		{
			scanf("%d%d%d",&l,&r,&k);
			up_add(1,1,n,l,r,k);
		}
		else if(op == 2)
		{
			scanf("%d%d%d",&l,&r,&k);
			up_mul(1,1,n,l,r,k);
		}
		else if(op == 3)
		{
			scanf("%d%d%d",&l,&r,&k);
			up_mul(1,1,n,l,r,0);
			up_add(1,1,n,l,r,k);
		}
		else if(op == 4)
		{
			scanf("%d",&k);
			up(1,1,n,++N,k);
		}
		else if(op == 5)
		{
			scanf("%d%d",&l,&r);
			printf("%lld\n",qu(1,1,n,l,r)%mod);
		}
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值