洛谷3373 线段树模板

题目详情:https://www.luogu.org/problemnew/show/P3373

这个线段树模板写的头疼(最后纠错发现一个long long没开差点一口血喷出来),思路就是在普通的求区间和线段树中使用两个update函数,一个负责加法,一个负责乘法(同理在线段树的结构体里自然也有addmark和mulmark)。这里我们将延迟标记乘的优先级设置高于延迟标记加,原因是当我需要给一段区间乘一个数,他并不不会因为之后这段区间上加了某一个数而使这个标记改变,但是我先加一个数后再乘一个数,根据mulmark的值,addmark就会有改变,具体看例子。

一个长度为5的数列

1 2 3 4 5

第一次我将它在区间[1,3]上乘以2,他在这段区间上的和直接先乘以2,[1,3]上的延迟标记乘变为2,而我再在区间[1,3]上加2,他除了在这段区间上的和加上了(3-1+1)*2,延迟标记加也变为2,然后我查询[1,2]的和,延迟标记往下传递,他的和就等于(2-1+1)*2+3*2=10(即延迟标记加乘以区间长度加上原值乘以延迟标记乘)。那我们将这个操作反过来,先在区间[1,3]上加2,再乘2,那么首先延迟标记加就会变成2,而在乘2的时候,除了延迟标记乘会变成2,延迟标记加也会变成2*2(延迟标记加*延迟标记乘),所以和为(2-1+1)*4+3*2=14。这是因为之后乘的那个数使之前的增加量也乘了相应的量,所以这样处理。

#include<stdio.h>
#include<stdlib.h>
long long a[5000001]={0};
int n,m,p;
struct seg{
    long long val;
    long long mulmark;
    long long addmark;
}seg[5000000];
int build(int root,int istart,int iend)
{
    int mid;
    seg[root].addmark=0;
    seg[root].mulmark=1;
    if(istart==iend)
    {
      seg[root].val=a[istart];
    }
    else
    {
    mid=(istart+iend)/2;
    build(root*2,istart,mid);
    build(root*2+1,mid+1,iend);
    seg[root].val=seg[root*2].val+seg[root*2+1].val;
    }
    seg[root].val=seg[root].val%=p;
    return 0;
}
long long quire(int root,int nstart,int nend,int qstart,int qend)
{
    int mid;
    if(nstart>qend||qstart>nend)
      return 0;
    if(qstart<=nstart&&qend>=nend)
      return seg[root].val;
    push(root,nstart,nend);
    mid=(nstart+nend)/2;
    return (quire(root*2,nstart,mid,qstart,qend)+quire(root*2+1,mid+1,nend,qstart,qend))%p;
}
int push(int root,int nstart,int nend)
{
    int mid;
    long long t1,t2;
    mid=(nstart+nend)/2;
    t1=seg[root].addmark;
    t2=seg[root].mulmark;
    seg[root*2].val=(seg[root*2].val*t2+t1*(mid-nstart+1))%p;
    seg[root*2+1].val=(seg[root*2+1].val*t2+t1*(nend-mid))%p;
    seg[root*2].addmark=(seg[root*2].addmark*t2+t1)%p;
    seg[root*2+1].addmark=(seg[root*2+1].addmark*t2+t1)%p;
    seg[root*2].mulmark*=t2;seg[root*2].mulmark%=p;
    seg[root*2+1].mulmark*=t2;seg[root*2+1].mulmark%=p;
    seg[root].addmark=0;seg[root].mulmark=1;
    return 0;
}
int updateadd(int root,int nstart,int nend,int ustart,int uend,long long add)
{
    int mid;
    if(ustart>nend||nstart>uend)
      return 0;
    if(ustart<=nstart&&uend>=nend)
      {
      seg[root].val+=(nend-nstart+1)*add;seg[root].val%=p;
      seg[root].addmark+=add;seg[root].addmark%=p;
      return 0;
      }
    push(root,nstart,nend);
    mid=(nstart+nend)/2;
    updateadd(root*2,nstart,mid,ustart,uend,add);
    updateadd(root*2+1,mid+1,nend,ustart,uend,add);
    seg[root].val=(seg[root*2].val+seg[root*2+1].val)%p;
    return 0;
}
int updatemul(int root,int nstart,int nend,int ustart,int uend,long long mul)
{
    int mid;
    if(ustart>nend||nstart>uend)
      return 0;
    if(ustart<=nstart&&uend>=nend)
      {
      seg[root].val*=mul;seg[root].val%=p;
      seg[root].mulmark*=mul;seg[root].mulmark%=p;
      seg[root].addmark*=mul;seg[root].addmark%=p;
      return 0;
      }
    push(root,nstart,nend);
    mid=(nstart+nend)/2;
    updatemul(root*2,nstart,mid,ustart,uend,mul);
    updatemul(root*2+1,mid+1,nend,ustart,uend,mul);
    seg[root].val=(seg[root*2].val+seg[root*2+1].val)%p;
    return 0;
}
int main()
{
    int i,flag,x,y;
    long long k;
    scanf("%d%d%d",&n,&m,&p);
    for(i=1;i<=n;i++)
      scanf("%lld",&a[i]);
    build(1,1,n);
    for(i=1;i<=m;i++)
    {
    scanf("%d",&flag);
      if(flag==1)
      {
      scanf("%d%d%lld",&x,&y,&k);
      updatemul(1,1,n,x,y,k);
      }
      else if(flag==2)
      {
      scanf("%d%d%lld",&x,&y,&k);
      updateadd(1,1,n,x,y,k);
      }
      else if(flag==3)
      {
      scanf("%d%d",&x,&y);
      printf("%lld\n",quire(1,1,n,x,y));
      }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40892508/article/details/81253444
今日推荐