洛谷 P2023 [AHOI2009]维护序列

洛谷传送门

题目描述

老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。 有长为N的数列,不妨设为a1,a2,…,aN 。有如下三种操作形式:

(1)把数列中的一段数全部乘一个值;

(2)把数列中的一段数全部加一个值;

(3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值。

输入格式

第一行两个整数N和P(1≤P≤1000000000)。

第二行含有N个非负整数,从左到右依次为a1,a2,…,aN, (0≤ai≤1000000000,1≤i≤N)。

第三行有一个整数M,表示操作总数。

从第四行开始每行描述一个操作,输入的操作有以下三种形式:

操作1:“1 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai×c(1≤t≤g≤N,0≤c≤1000000000)。

操作2:“2 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai+c (1≤t≤g≤N,0≤c≤1000000000)。

操作3:“3 t g”(不含双引号)。询问所有满足t≤i≤g的ai的和模P的值 (1≤t≤g≤N)。

同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。

输出格式

对每个操作3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。

输入输出样例

输入 #1复制

输出 #1复制

说明/提示

【样例说明】

初始时数列为(1,2,3,4,5,6,7)。

经过第1次操作后,数列为(1,10,15,20,25,6,7)。

对第2次操作,和为10+15+20=45,模43的结果是2。

经过第3次操作后,数列为(1,10,24,29,34,15,16}

对第4次操作,和为1+10+24=35,模43的结果是35。

对第5次操作,和为29+34+15+16=94,模43的结果是8。

测试数据规模如下表所示

数据编号 1 2 3 4 5 6 7 8 9 10

N= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000

M= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000

Source: Ahoi 2009

题解:

线段树模板维护区间乘和区间加,查询区间和。

如果对于简单线段树不是很了解的同学可以翻看本蒟蒻的这篇博客:

详解简单线段树

但是简单线段树并没有给出区间乘法的维护方法...

但我们可以参照区间加法进行类比。区间加法需要维护一个延迟标记,区间乘法当然也需要,由于乘法就是一连串的加法,所以我们在下传延迟标记的时候不仅需要下传乘法标记给线段树数组和子节点的乘法标记数组,还要把乘法标记下传给加法标记数组。也就是说:

线段树维护区间乘法的步骤为:

线段树数组乘标记,自己乘标记,加法数组乘标记。

加法的直接上简单线段树的板子即可。

注意,先乘后加!

代码:

#include<cstdio>
#define int long long
#define lson pos<<1
#define rson pos<<1|1
using namespace std;
const int maxn=1e5+10;
int n,m,mod;
int a[maxn];
int tree[maxn<<2],lazy[maxn<<2],lazy2[maxn<<2];
void build(int pos,int l,int r)
{
int mid=(l+r)>>1;
if(l==r)
{
tree[pos]=a[l];
return;
}
build(lson,l,mid);
build(rson,mid+1,r);
tree[pos]=(tree[lson]+tree[rson])%mod;
}
void mark(int pos,int l,int r,int k,int flag)
{
if(flag)
{
tree[pos]=(tree[pos]*k)%mod;
lazy2[pos]=(lazy2[pos]*k)%mod;
lazy[pos]=(lazy[pos]*k)%mod;
}
else
{
tree[pos]=(tree[pos]+(r-l+1)*k)%mod;
lazy2[pos]=(lazy2[pos]+k)%mod;
}
}
void pushdown(int pos,int l,int r)
{
int mid=(l+r)>>1;
mark(lson,l,mid,lazy[pos],1);
mark(rson,mid+1,r,lazy[pos],1);
mark(lson,l,mid,lazy2[pos],0);
mark(rson,mid+1,r,lazy2[pos],0);
lazy2[pos]=0,lazy[pos]=1;
}
void update(int pos,int l,int r,int x,int y,int k,int flag)
{
int mid=(l+r)>>1;
if(x<=l && r<=y)
{
if(flag)
mark(pos,l,r,k,flag);
else
mark(pos,l,r,k,flag);
return;
}
pushdown(pos,l,r);
if(x<=mid)
update(lson,l,mid,x,y,k,flag);
if(y>mid)
update(rson,mid+1,r,x,y,k,flag);
tree[pos]=(tree[lson]+tree[rson])%mod;
}
int query(int pos,int l,int r,int x,int y)
{
int ret=0;
int mid=(l+r)>>1;
if(x<=l && r<=y)
return tree[pos]%mod;
pushdown(pos,l,r);
if(x<=mid)
ret=(ret+query(lson,l,mid,x,y))%mod;
if(y>mid)
ret=(ret+query(rson,mid+1,r,x,y))%mod;
return ret%mod;
}
signed main()
{
for(int i=1;i<=maxn<<2;i++)
lazy[i]=1,lazy2[i]=0;
scanf("%lld%lld",&n,&mod);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
build(1,1,n);
scanf("%lld",&m);
for(int i=1;i<=m;i++)
{
int opt,x,y,k;
scanf("%lld",&opt);
if(opt==1)
{
scanf("%lld%lld%lld",&x,&y,&k);
update(1,1,n,x,y,k,1);
}
else if(opt==2)
{
scanf("%lld%lld%lld",&x,&y,&k);
update(1,1,n,x,y,k,0);
}
else
{
scanf("%lld%lld",&x,&y);
printf("%lld\n",query(1,1,n,x,y));
}
}
return 0;
}

最新文章

  1. C++初学者 const使用详解
  2. jquery.form.js详细讲解
  3. FreePascal经典资料
  4. win7重新安装win7
  5. AngularJS 父子控制器
  6. imadjust函数分析一
  7. XXTEA 加密算法 C++ C#兼容版本号
  8. 学习新手给Android新手的一些学习建议
  9. python+django+bootstrap
  10. SparkR安装部署及数据分析实例
  11. Mysql分页处理(PageHelper)
  12. netsh用法
  13. Python函数中如何定义参数
  14. rsync 自动创建目录的坑点
  15. MSSQL 复制数据 并随机打乱写入
  16. 20175209 《Java程序设计》第四周学习总结
  17. html5 css练习 下拉菜单制作
  18. xmanager 连接centos 7桌面
  19. linux-shell系列5-统计
  20. 保存 laravel model 而不更新 timestamps 的方法

热门文章

  1. 为什么 netstat 对某些服务只显示了 tcp6 监听端口
  2. MySQL数据库:合并结果集
  3. Python导入运行的当前模块报错
  4. canvas应用
  5. Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3
  6. SQL Server 索引分析开关
  7. 使用python解析ip地址
  8. SpringBoot系列之日志框架使用教程
  9. Python三级菜单作业实现
  10. Jmeter参数化-CSV Data Set Config