【bzoj1798】[Ahoi2009]Seq 维护序列seq

大意:一个数组,三个操作,第一种是区间[a,b]每个数乘乘,第二种是区间[a,b]每个数加c,第三种是查询[a,b]区间的和并对p取摸。

两种操作就不能简单的只往下传标记。每次传乘法标记时,要把加法标记同时乘上乘法标记,例如某个区间先进来一个加法标记add,之后又进来一个乘法标记mul。

那么结果为(x+add)*mul=x*mul+add*mul。这样向下传标记的时候就相对独立。递归边界更新加法标记之前先乘上该节点的mul,左右儿子down的时候先将儿子的add乘上本节点的mul。

最后说一下sum,比如本节点的存在加法标记x和乘法标记y,并且是先加上x,再乘上y,则左儿子的sum要更新为(sum+x)*y。由于乘法标记传到本节点的时候更新了加法标记,x =x*y,所以sum[o<<1]=(左区间的长度*x)+sum[o<<1]*y。

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstdlib>
  4 #include<cstring>
  5 #include<cstdio>
  6 #include<cmath>
  7 using namespace std;
  8
  9 typedef long long LL;
 10
 11 #define N 100010
 12
 13 int n,m;
 14 int askd,al,ar,ask;
 15 LL p;
 16
 17 LL add[N<<2],sum[N<<2],mul[N<<2];
 18
 19 void pushup(int now)
 20 {
 21     sum[now]=(sum[now<<1]+sum[now<<1|1])%p;
 22 }
 23
 24 void pushdown(int now,int d)
 25 {
 26     if (add[now]!=0 || mul[now]!=1)
 27     {
 28         mul[now<<1]=mul[now<<1]*mul[now]%p;
 29         mul[now<<1|1]=mul[now<<1|1]*mul[now]%p;
 30         add[now<<1]=(add[now]+add[now<<1]*mul[now])%p;
 31         add[now<<1|1]=(add[now]+add[now<<1|1]*mul[now])%p;
 32         sum[now<<1]=(add[now]*(d-(d>>1))+sum[now<<1]*mul[now])%p;
 33         sum[now<<1|1]=(add[now]*(d>>1)+sum[now<<1|1]*mul[now])%p;
 34         add[now]=0;
 35         mul[now]=1;
 36     }
 37 }
 38
 39 void build(int nowl,int nowr,int now)
 40 {
 41     sum[now]=0;
 42     mul[now]=1;
 43     if (nowl==nowr)
 44     {
 45         scanf("%lld",&sum[now]);
 46         return ;
 47     }
 48     int mid=(nowl+nowr)>>1;
 49     build(nowl,mid,now<<1);
 50     build(mid+1,nowr,now<<1|1);
 51     pushup(now);
 52 }
 53
 54 void updata_mul(int nowl,int nowr,int now,int l,int r,int c)
 55 {
 56     if (nowl>=l && nowr<=r)
 57     {
 58         add[now]=add[now]*c%p;
 59         sum[now]=sum[now]*c%p;
 60         mul[now]=mul[now]*c%p;
 61         return ;
 62     }
 63     pushdown(now,nowr-nowl+1);
 64     int mid=(nowl+nowr)>>1;
 65     if (l<=mid)
 66         updata_mul(nowl,mid,now<<1,l,r,c);
 67     if (r>mid)
 68         updata_mul(mid+1,nowr,now<<1|1,l,r,c);
 69     pushup(now);
 70 }
 71
 72 void updata_add(int nowl,int nowr,int now,int l,int r,int c)
 73 {
 74     if (nowl>=l && nowr<=r)
 75     {
 76         add[now]=(add[now]+c)%p;
 77         sum[now]=(sum[now]+c*(nowr-nowl+1))%p;
 78         return ;
 79     }
 80     pushdown(now,nowr-nowl+1);
 81     int mid=(nowl+nowr)>>1;
 82     if (l<=mid)
 83         updata_add(nowl,mid,now<<1,l,r,c);
 84     if (r>mid)
 85         updata_add(mid+1,nowr,now<<1|1,l,r,c);
 86     pushup(now);
 87 }
 88
 89 LL query(int nowl,int nowr,int now,int l,int r)
 90 {
 91     LL res(0);
 92     if (nowl>=l && nowr<=r)
 93         return sum[now];
 94     pushdown(now,nowr-nowl+1);
 95     int mid=(nowl+nowr)>>1;
 96     if (l<=mid)
 97         res=(res+query(nowl,mid,now<<1,l,r))%p;
 98     if (r>mid)
 99         res=(res+query(mid+1,nowr,now<<1|1,l,r))%p;
100     return res;
101 }
102
103 int main()
104 {
105     scanf("%d%lld",&n,&p);
106     build(1,n,1);
107     scanf("%d",&m);
108     while (m--)
109     {
110         scanf("%d",&askd);
111         if (askd==1)
112         {
113             scanf("%d%d%d",&al,&ar,&ask);
114             updata_mul(1,n,1,al,ar,ask);
115         }
116         if (askd==2)
117         {
118             scanf("%d%d%d",&al,&ar,&ask);
119             updata_add(1,n,1,al,ar,ask);
120         }
121         if (askd==3)
122         {
123             scanf("%d%d",&al,&ar);
124             printf("%lld\n",query(1,n,1,al,ar));
125         }
126     }
127     return 0;
128 }
时间: 2024-08-29 06:37:15

【bzoj1798】[Ahoi2009]Seq 维护序列seq的相关文章

bzoj1798[Ahoi2009]Seq 维护序列seq

bzoj1798[Ahoi2009]Seq 维护序列seq 题意: 维护序列,支持区间加.区间乘.区间求和模一个数.序列大小和操作数≤100000 题解: 线段树,加标记和乘标记的处理同bzoj4003.模的时候注意细节. 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define m

bzoj1798: [Ahoi2009]Seq 维护序列seq(线段树多重标记下传)

www.cnblogs.com/shaokele/ bzoj1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec Memory Limit: 64 MB Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,-,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值

bzoj1798: [Ahoi2009]Seq 维护序列seq 2011-12-20

1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec  Memory Limit: 64 MB Submit: 497  Solved: 203 [Submit][Status][Discuss] Description 老 师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的

1798: [Ahoi2009]Seq 维护序列seq

1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec  Memory Limit: 64 MBSubmit: 5886  Solved: 2087[Submit][Status][Discuss] Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和

bzoj 1798: [Ahoi2009]Seq 维护序列seq 线段树 区间乘法区间加法 区间求和

1798: [Ahoi2009]Seq 维护序列seq Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=1798 Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列

bzoj1798: [Ahoi2009]Seq 维护序列seq(线段树)

bzoj1798 题目描述:给定n个数的序列,有三种操作. ?????1.将一段区间乘上c ?????2.将一段区间加上c ?????3.求一段区间的和 输入格式:第一行两个整数第一行两个整数N和P(1≤P≤1000000000).第二行含有N个非负整数,从左到右依次为a1,a2,-,aN, (0≤ai≤1000000000,1≤i≤N).第三行有一个整数M,表示操作总数.从第四行开始每行描述一个操作,输入的操作有以下三种形式: 操作1:"1 t g c"(不含双引号).表示把所有满足

BZOJ1798[Ahoi2009]Seq 维护序列seq 题解

题目大意: 有长为N的数列,有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值. 思路: 用线段树来维护当前的值和要加以及乘的值,由于加与乘是有序的,所以要在做子树之前将标记下传(注意顺序),加和乘分开来.合起来处理都可以. 代码:(当初手抽将1打成l一直RE调了半天才发现) 1 #include<cstdio> 2 #include<cstring> 3 #in

【分块】bzoj1798 [Ahoi2009]Seq 维护序列seq

分块,打标记,维护两个标记:乘的 和 加的. 每次 区间乘的时候,对 乘标记 和 加标记 都 乘上那个值. 每次 区间加的时候 对 加标记 加上那个值. (ax+b)*v=axv+bv.开 long long. 1 #include<cstdio> 2 #include<cmath> 3 using namespace std; 4 typedef long long ll; 5 int n,sum,sz,num[100001],l[350],r[350],x,y,m,op; 6

bzoj1798: [Ahoi2009]Seq 维护序列seq 线段树

题目传送门 这道题就是线段树 先传乘法标记再传加法 #include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; const int M=400010; LL read(){ LL ans=0,f=1,c=getchar(); while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}