luogu3373线段树2

作为模板题,这题简直不是人.

因为这道题涉及到了一个很严肃的问题,就是两个tag.

那么乘法tag和加法tag的处理就很关键了.

那么题目如下:

给定一个序列,要求支持三种操作:

1.给某一段加一个数.

2.给某一段乘一个数.

3.查询某一段的和.

那么这道题主要就是两个tag怎么处理他们的优先级关系.

这道题我采用的方案是让乘法优先.

分两种情况处理:

1.正在处理操作2,那么就将乘法tag乘上的同时,也将加法tag乘一个数,能够这样处理是因为a*(b+c)=a*b+a*c.

2.正在处理操作1,那么就将加法tag直接把乘法标记先处理到加法标记里去,再把num加进去.

然后开个longlong,查询的时候直接先调用乘法tag,再调用加法tag.

pushdown部分十分重要,这里单独拿出来谈一下.

因为我们知道,乘法标记的优先级高,肯定是都要先乘一下的,所以先处理乘法标记光给下面ls和rs的sum乘.

接下来直接吧加法标记直接推下去加.

那么pushdown部分如下:

void pushdown(int k){
  int ls=k<<1,rs=ls|1;
  tr[ls].sum=tr[ls].sum*tr[k].tag2%p;
  tr[rs].sum=tr[rs].sum*tr[k].tag2%p;
  tr[ls].tag2=tr[ls].tag2*tr[k].tag2%p;
  tr[rs].tag2=tr[rs].tag2*tr[k].tag2%p;
  tr[ls].tag1=tr[ls].tag1*tr[k].tag2%p;
  tr[rs].tag1=tr[rs].tag1*tr[k].tag2%p;
  tr[k].tag2=1LL;
  tr[ls].sum=(tr[ls].sum+tr[k].tag1*(tr[ls].r-tr[ls].l+1))%p;
  tr[rs].sum=(tr[rs].sum+tr[k].tag1*(tr[rs].r-tr[rs].l+1))%p;
  tr[ls].tag1=(tr[ls].tag1+tr[k].tag1)%p;
  tr[rs].tag1=(tr[rs].tag1+tr[k].tag1)%p;
  tr[k].tag1=0LL;
}

那么线段树再奉上pushup:

void pushup(int k){
  tr[k].sum=(tr[k<<1].sum+tr[k<<1|1].sum)%p;
}

再来个建树:

struct tree{
  int l,r;
  long long tag1,tag2,sum;
}tr[500001];
void build(int L,int R,int k=1){
  tr[k].l=L;tr[k].r=R;
  tr[k].tag1=tr[k].sum=0LL;
  tr[k].tag2=1LL;      //由于是乘法标记
  if (L==R) return;
  int mid=L+R>>1;
  build(L,mid,k<<1);
  build(mid+1,R,k<<1|1); 
}

那么先乘法修改处理,这个很简单因为乘法优先级高,就直接搞:

void mul(int L,int R,long long num,int k=1){
  if (tr[k].l==L&&tr[k].r==R){
    tr[k].sum=tr[k].sum*num%p;
    tr[k].tag1=tr[k].tag1*num%p;
    tr[k].tag2=tr[k].tag2*num%p;
    return;
  }
  pushdown(k);
  int mid=tr[k].l+tr[k].r>>1;
  if (R<=mid) mul(L,R,num,k<<1);
  else if (L>mid) mul(L,R,num,k<<1|1);
    else mul(L,mid,num,k<<1),mul(mid+1,R,num,k<<1|1);
  pushup(k);
}

加法修改处理稍微复杂一点.

具体代码如下:

void add(int L,int R,long long num,int k=1){
  if (L==tr[k].l&&R==tr[k].r){
    tr[k].tag1=(tr[k].tag1+num)%p;
    tr[k].sum=(tr[k].sum+(tr[k].r-tr[k].l+1)*num)%p;
    return;
  }
  pushdown(k);
  int mid=tr[k].l+tr[k].r>>1;
  if (R<=mid) add(L,R,num,k<<1);
  else if (L>mid) add(L,R,num,k<<1|1);
    else add(L,mid,num,k<<1),add(mid+1,R,num,k<<1|1);
  pushup(k);
}

那么查询好说,直接调用pushdown:

int query(int L,int R,int k=1){
  if (L==tr[k].l&&R==tr[k].r) return tr[k].sum%p;
  pushdown(k);
  int mid=tr[k].l+tr[k].r>>1;
  if (R<=mid) return query(L,R,k<<1);
  else if (L>mid) return query(L,R,k<<1|1);
    else return query(L,mid,k<<1)+query(mid+1,R,k<<1|1);
}

那么AC代码如下:

#include<bits/stdc++.h>
  using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)
int n,m;
long long p;
long long a[100001]={0};
struct tree{
  int l,r;
  long long tag1,tag2,sum;
}tr[500001];
void build(int L,int R,int k=1){
  tr[k].l=L;tr[k].r=R;
  tr[k].tag1=tr[k].sum=0LL;
  tr[k].tag2=1LL;      //由于是乘法标记
  if (L==R) return;
  int mid=L+R>>1;
  build(L,mid,k<<1);
  build(mid+1,R,k<<1|1); 
}
void pushup(int k){
  tr[k].sum=(tr[k<<1].sum+tr[k<<1|1].sum)%p;
}
void pushdown(int k){
  int ls=k<<1,rs=ls|1;
  tr[ls].sum=tr[ls].sum*tr[k].tag2%p;
  tr[rs].sum=tr[rs].sum*tr[k].tag2%p;
  tr[ls].tag2=tr[ls].tag2*tr[k].tag2%p;
  tr[rs].tag2=tr[rs].tag2*tr[k].tag2%p;
  tr[ls].tag1=tr[ls].tag1*tr[k].tag2%p;
  tr[rs].tag1=tr[rs].tag1*tr[k].tag2%p;
  tr[k].tag2=1LL;
  tr[ls].sum=(tr[ls].sum+tr[k].tag1*(tr[ls].r-tr[ls].l+1))%p;
  tr[rs].sum=(tr[rs].sum+tr[k].tag1*(tr[rs].r-tr[rs].l+1))%p;
  tr[ls].tag1=(tr[ls].tag1+tr[k].tag1)%p;
  tr[rs].tag1=(tr[rs].tag1+tr[k].tag1)%p;
  tr[k].tag1=0LL;
}
void mul(int L,int R,long long num,int k=1){
  if (tr[k].l==L&&tr[k].r==R){
    tr[k].sum=tr[k].sum*num%p;
    tr[k].tag1=tr[k].tag1*num%p;
    tr[k].tag2=tr[k].tag2*num%p;
    return;
  }
  pushdown(k);
  int mid=tr[k].l+tr[k].r>>1;
  if (R<=mid) mul(L,R,num,k<<1);
  else if (L>mid) mul(L,R,num,k<<1|1);
    else mul(L,mid,num,k<<1),mul(mid+1,R,num,k<<1|1);
  pushup(k);
}
void add(int L,int R,long long num,int k=1){
  if (L==tr[k].l&&R==tr[k].r){
    tr[k].tag1=(tr[k].tag1+num)%p;
    tr[k].sum=(tr[k].sum+(tr[k].r-tr[k].l+1)*num)%p;
    return;
  }
  pushdown(k);
  int mid=tr[k].l+tr[k].r>>1;
  if (R<=mid) add(L,R,num,k<<1);
  else if (L>mid) add(L,R,num,k<<1|1);
    else add(L,mid,num,k<<1),add(mid+1,R,num,k<<1|1);
  pushup(k);
}
int query(int L,int R,int k=1){
  if (L==tr[k].l&&R==tr[k].r) return tr[k].sum%p;
  pushdown(k);
  int mid=tr[k].l+tr[k].r>>1;
  if (R<=mid) return query(L,R,k<<1);
  else if (L>mid) return query(L,R,k<<1|1);
    else return query(L,mid,k<<1)+query(mid+1,R,k<<1|1);
}
inline void into(){
  scanf("%d%d%lld",&n,&m,&p);
  build(1,n);
  rep(i,1,n){
    scanf("%lld",&a[i]);
    add(i,i,a[i]%p);
  }
}
inline void work(){
}
inline void outo(){
  int L,R,opt;
  long long num;
  rep(i,1,m){
    scanf("%d",&opt);
    if (opt==1){
      scanf("%d%d%lld",&L,&R,&num);
      mul(L,R,num%p);
    }else if (opt==2){
      scanf("%d%d%lld",&L,&R,&num);
      add(L,R,num%p);
    }else{
      scanf("%d%d",&L,&R);
      printf("%lld\n",query(L,R)%p);
    }
  }
}
int main(){
  into();
  work();
  outo();
  return 0;
}

阅读更多
个人分类: 线段树
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

luogu3373线段树2

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭