洛谷3373 线段树2题解(线段树)

题目:luogu3373.
题目大意:给定一个长度为 n n n的序列,要求支持三种操作:
1.给某一段加一个数.
2.给某一段乘一个数.
3.查询某一段的和.
设操作数为 m m m 1 ≤ n , m ≤ 1 0 5 1\leq n,m\leq 10^5 1n,m105.

首先我们很容易用线段树维护没有区间乘法操作的情况,根据套路我们可以再给乘法搞个tag.

然后我们发现,加法tag和乘法tag撞在一起有点棘手…

其实这种情况很容易处理,我们直接钦定乘法会对加法标记产生影响,而加法不会对乘法标记产生影响即可.

也就是说每次打乘法标记的时候,顺便也给加法标记乘上乘法标记的贡献,加法标记照样打.

然后这道题就解决啦.时间复杂度 O ( n + m log ⁡ n ) O(n+m\log n) O(n+mlogn).

那么AC代码如下:

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=100000;

int n,m;
LL mod,a[N+9];
struct tree{
  LL sum,add,mul;
  int len;
}tr[N*4+9];

LL add(LL a,LL b){return a+b>=mod?a+b-mod:a+b;}
LL mul(LL a,LL b){return a*b%mod;}
void sadd(LL &a,LL b){a=add(a,b);}
void smul(LL &a,LL b){a=mul(a,b);}

void Pushup(int k){tr[k].sum=add(tr[k<<1].sum,tr[k<<1|1].sum);}
void Update_add(int k,LL v){sadd(tr[k].sum,mul(v,(LL)tr[k].len));sadd(tr[k].add,v);}

void Pushdown_add(int k){
  if (!tr[k].add) return;
  Update_add(k<<1,tr[k].add);Update_add(k<<1|1,tr[k].add);
  tr[k].add=0;
}

void Update_mul(int k,LL v){smul(tr[k].sum,v);smul(tr[k].mul,v);smul(tr[k].add,v);}

void Pushdown_mul(int k){
  if (tr[k].mul==1) return;
  Update_mul(k<<1,tr[k].mul);Update_mul(k<<1|1,tr[k].mul);
  tr[k].mul=1;
}

void Pushdown(int k){Pushdown_mul(k);Pushdown_add(k);}

void Build(int L,int R,int k){
  tr[k].len=R-L+1;tr[k].mul=1;
  if (L==R) {tr[k].sum=a[L]%mod;return;}
  int mid=L+R>>1;
  Build(L,mid,k<<1);Build(mid+1,R,k<<1|1);
  Pushup(k);
}

void Change_add(int L,int R,LL v,int l,int r,int k){
  if (L==l&&R==r) {Update_add(k,v);return;}
  Pushdown(k);
  int mid=l+r>>1;
  if (R<=mid) Change_add(L,R,v,l,mid,k<<1);
  else if (L>mid) Change_add(L,R,v,mid+1,r,k<<1|1);
    else Change_add(L,mid,v,l,mid,k<<1),Change_add(mid+1,R,v,mid+1,r,k<<1|1);
  Pushup(k);
}

void Change_mul(int L,int R,LL v,int l,int r,int k){
  if (L==l&&R==r) {Update_mul(k,v);return;}
  Pushdown(k);
  int mid=l+r>>1;
  if (R<=mid) Change_mul(L,R,v,l,mid,k<<1);
  else if (L>mid) Change_mul(L,R,v,mid+1,r,k<<1|1);
    else Change_mul(L,mid,v,l,mid,k<<1),Change_mul(mid+1,R,v,mid+1,r,k<<1|1);
  Pushup(k);
}

LL Query_sum(int L,int R,int l,int r,int k){
  if (L==l&&R==r) return tr[k].sum;
  Pushdown(k);
  int mid=l+r>>1;
  if (R<=mid) return Query_sum(L,R,l,mid,k<<1);
  else if (L>mid) return Query_sum(L,R,mid+1,r,k<<1|1);
    else return add(Query_sum(L,mid,l,mid,k<<1),Query_sum(mid+1,R,mid+1,r,k<<1|1));
}

Abigail into(){
  scanf("%d%d%lld",&n,&m,&mod);
  for (int i=1;i<=n;++i)
    scanf("%lld",&a[i]); 
}

Abigail work(){
  Build(1,n,1);
}

Abigail outo(){
  int opt,l,r,x;
  for (int i=1;i<=m;++i){
  	scanf("%d%d%d",&opt,&l,&r);
	switch (opt){
	  case 1:
	  	scanf("%d",&x);
	  	Change_mul(l,r,(LL)x,1,n,1);
	  	break;
	  case 2:
	  	scanf("%d",&x);
	  	Change_add(l,r,(LL)x,1,n,1);
	  	break;
	  case 3:
	  	printf("%lld\n",Query_sum(l,r,1,n,1));
	  	break;
	} 
  } 
}

int main(){
  into();
  work();
  outo();
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值