线段树lazy标记2

来源:互联网 发布:centos nfs samba 编辑:程序博客网 时间:2024/06/05 01:03

Description
给定一个正整数序列A,要求支持以下操作:
1):ADD a b c 表示在[ab]上加上一个常数C
2):COVER a b c[ab]整体赋值为一个常数K
3):QUERY a b 查询[ab]sum

Input
第一行两个正整数nmn表示序列长度,m表示操作数。
第二行n个正整数,第i表示Ai的大小。
接下来的m行,每行有且仅有一种操作,具体和题目描述一致。
n,m<=100000,其他权值都<=50000,小心爆int。

Output
对于每个询问操作,输出答案。

Sample Input
10 10
17 18 16 12 13 7 13 6 11 20
QUERY 5 6
QUERY 2 7
ADD 1 6 13
QUERY 4 10
ADD 2 5 18
COVER 2 8 11
ADD 6 9 5
QUERY 8 8
ADD 6 9 18
QUERY 5 7

Sample Output
20
79
121
16
79

HINT

思路
记录3个标记:icoveri表示这个点是否在下传标记后被覆盖。coveri表示这个点被覆盖成了什么值。sumi表示这个点被增加了多少。下传时先考虑cover的下传,再考虑sum的下传。注意每次一个点被覆盖后这个点的sum值都要被赋成0

代码

#include <cstdio>const int maxn=100000;long long a[maxn+10];struct segment_tree{  long long val[(maxn<<2)+10],sum[(maxn<<2)+10],cover[(maxn<<2)+10];  int icover[(maxn<<2)+10];  inline int updata(int now)//更新这个点的val  {    val[now]=val[now<<1]+val[now<<1|1];    return val[now];  }  inline int pushdown(int now,int left,int right)//下传标记  {    int mid=(left+right)>>1;    if(icover[now])//如果这个点被cover过,下传cover标记      {        icover[now]=0;        cover[now<<1]=cover[now];        sum[now<<1]=0;//注意sum标记要赋成0        val[now<<1]=cover[now]*(mid-left+1);        cover[now<<1|1]=cover[now];        sum[now<<1|1]=0;        val[now<<1|1]=cover[now]*(right-mid);        icover[now<<1]=icover[now<<1|1]=1;//表示被覆盖过      }    sum[now<<1]+=sum[now];//下传sum标记    val[now<<1]+=sum[now]*(mid-left+1);    sum[now<<1|1]+=sum[now];    val[now<<1|1]+=sum[now]*(right-mid);    sum[now]=0;    return 0;  }  int build(int now,int left,int right)//建树  {    if(left==right)      {        icover[now]=0;//初始化        sum[now]=0;        val[now]=a[left];        return 0;      }    int mid=(left+right)>>1;    build(now<<1,left,mid);    build(now<<1|1,mid+1,right);    updata(now);    return 0;  }  int rcover(int now,int left,int right,int askl,int askr,long long cval)  //将一个区间[left,right]cover成cval  {    if((askl<=left)&&(right<=askr))      {        icover[now]=1;//覆盖        cover[now]=cval;        sum[now]=0;//注意        val[now]=cval*(right-left+1);        return 0;      }    int mid=(left+right)>>1;    pushdown(now,left,right);    if(askl<=mid)      {        rcover(now<<1,left,mid,askl,askr,cval);      }    if(mid<askr)      {        rcover(now<<1|1,mid+1,right,askl,askr,cval);      }    updata(now);    return 0;  }  int add(int now,int left,int right,int askl,int askr,long long cval)  //将一个区间的值增加  {    if((askl<=left)&&(right<=askr))      {        sum[now]+=cval;        val[now]+=cval*(right-left+1);        return 0;      }    int mid=(left+right)>>1;    pushdown(now,left,right);    if(askl<=mid)      {        add(now<<1,left,mid,askl,askr,cval);      }    if(mid<askr)      {        add(now<<1|1,mid+1,right,askl,askr,cval);      }    updata(now);    return 0;  }  long long getsum(int now,int left,int right,int askl,int askr)  //求一段区间的和  {    if((askl<=left)&&(right<=askr))      {        return val[now];      }    int mid=(left+right)>>1;    long long res=0;    pushdown(now,left,right);    if(askl<=mid)      {        res+=getsum(now<<1,left,mid,askl,askr);      }    if(mid<askr)      {        res+=getsum(now<<1|1,mid+1,right,askl,askr);      }    return res;  }};segment_tree st;int n,m,aa,b;long long c;char s[10];int main(){  scanf("%d%d",&n,&m);  for(register int i=1; i<=n; ++i)    {      scanf("%lld",&a[i]);    }  st.build(1,1,n);  while(m--)    {      scanf("%s%d%d",s,&aa,&b);      if(s[0]=='Q')        {          printf("%lld\n",st.getsum(1,1,n,aa,b));        }      else if(s[0]=='C')        {          scanf("%lld",&c);          st.rcover(1,1,n,aa,b,c);        }      else        {          scanf("%lld",&c);          st.add(1,1,n,aa,b,c);        }    }  return 0;}
原创粉丝点击