【分块】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 ll MOD,a[100001],sumv[350],lzy1[350],lzy2[350],v;
  7 void makeblock()
  8 {
  9     sz=sqrt(n); if(!sz) sz=1;
 10     for(sum=1;sum*sz<n;sum++)
 11       {
 12           l[sum]=r[sum-1]+1; r[sum]=sum*sz;
 13           for(int i=l[sum];i<=r[sum];++i)
 14           {
 15               num[i]=sum;
 16               sumv[sum]=(sumv[sum]+a[i])%MOD;
 17           }
 18       }
 19     l[sum]=r[sum-1]+1; r[sum]=n;
 20     for(int i=l[sum];i<=r[sum];++i)
 21       {
 22           num[i]=sum;
 23           sumv[sum]=(sumv[sum]+a[i])%MOD;
 24       }
 25     for(int i=1;i<=sum;++i) lzy1[i]=1;
 26 }
 27 void pushdown(const int &p)
 28 {
 29     if(lzy1[p]!=1 || lzy2[p])
 30       {
 31           for(int i=l[p];i<=r[p];++i)
 32           a[i]=(a[i]*lzy1[p]+lzy2[p])%MOD;
 33           lzy1[p]=1; lzy2[p]=0;
 34       }
 35 }
 36 void work1(const int &L,const int &R)
 37 {
 38     sumv[num[L]]=0;
 39     for(int i=L;i<=R;++i) a[i]=(a[i]*v)%MOD;
 40     for(int i=l[num[L]];i<=r[num[L]];++i)
 41       sumv[num[L]]=(sumv[num[L]]+a[i])%MOD;
 42 }
 43 void update1()
 44 {
 45     pushdown(num[x]); pushdown(num[y]);
 46     if(num[x]==num[y]) work1(x,y);
 47     else
 48       {
 49           work1(x,r[num[x]]); work1(l[num[y]],y);
 50           for(int i=num[x]+1;i<num[y];++i)
 51             {
 52                 lzy1[i]=(lzy1[i]*v)%MOD;
 53                 lzy2[i]=(lzy2[i]*v)%MOD;
 54                 sumv[i]=(sumv[i]*v)%MOD;
 55             }
 56       }
 57 }
 58 void work2(const int &L,const int &R)
 59 {
 60     for(int i=L;i<=R;++i) a[i]=(a[i]+v)%MOD;
 61     sumv[num[L]]=(sumv[num[L]]+(ll)(R-L+1)*v)%MOD;
 62 }
 63 void update2()
 64 {
 65     pushdown(num[x]); pushdown(num[y]);
 66     if(num[x]==num[y]) work2(x,y);
 67     else
 68       {
 69           work2(x,r[num[x]]); work2(l[num[y]],y);
 70           for(int i=num[x]+1;i<num[y];++i)
 71           {
 72               lzy2[i]=(lzy2[i]+v)%MOD;
 73               sumv[i]=(sumv[i]+(ll)sz*v)%MOD;
 74           }
 75       }
 76 }
 77 void query()
 78 {
 79     pushdown(num[x]); pushdown(num[y]); ll ans=0;
 80     if(num[x]==num[y]) {for(int i=x;i<=y;++i) ans=(ans+a[i])%MOD;}
 81     else
 82       {
 83           for(int i=x;i<=r[num[x]];++i) ans=(ans+a[i])%MOD;
 84           for(int i=l[num[y]];i<=y;++i) ans=(ans+a[i])%MOD;
 85           for(int i=num[x]+1;i<num[y];++i) ans=(ans+sumv[i])%MOD;
 86       } printf("%d\n",(int)ans);
 87 }
 88 int main()
 89 {
 90     scanf("%d%lld",&n,&MOD);
 91     for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
 92     makeblock(); scanf("%d",&m);
 93     for(;m>0;--m)
 94       {
 95           scanf("%d%d%d",&op,&x,&y);
 96           if(op==1) {scanf("%lld",&v); update1();}
 97           else if(op==2) {scanf("%lld",&v); update2();}
 98           else query();
 99       }
100     return 0;
101 }
时间: 2024-09-30 18:38:43

【分块】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 线段树

题目传送门 这道题就是线段树 先传乘法标记再传加法 #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();}

【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. 最后说一下