【题解】洛谷P2023 [AHOI2009] 维护序列(线段树)
洛谷P2023:https://www.luogu.org/problemnew/show/P2023
思路
需要2个Lazy-Tag
一个表示加的 一个表示乘的
需要先计算乘法 再计算加法
来自你谷milkfilling大佬的解释:
①加法优先,即规定好segtree[root*2].value=((segtree[root*2].value+segtree[root].add)*segtree[root].mul)%p,问题是这样的话非常不容易进行更新操作,假如改变一下add的数值,mul也要联动变成奇奇怪怪的分数小数损失精度,我们内心是很拒绝的;
②乘法优先,即规定好segtree[root*2].value=(segtree[root*2].value*segtree[root].mul+segtree[root].add*(本区间长度))%p,这样的话假如改变add的数值就只改变add,改变mul的时候把add也对应的乘一下就可以了,没有精度损失,看起来很不错。
所以这道题就是模板题啦~
易错点会在代码中详细注释出来
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 100007
#define ll long long
ll sum[maxn<<],add[maxn<<],mul[maxn<<],a[maxn],n,m,p;
void build(ll l,ll r,ll k)
{
mul[k]=;//初始化标记
if(l==r)
{
sum[k]=a[l]%p;
mul[k]=;
return;
}
ll mid=(l+r)>>;
build(l,mid,k<<);//左右子树构建
build(mid+,r,k<<|);
sum[k]=(sum[k<<]+sum[k<<|])%p;//维护sum值
}
void pushdown(ll l,ll r,ll mid,ll k)//标记下放
{
//先乘后加
if(mul[k]!=)//当有乘法标记 计算所有的值
{
sum[k<<]=(sum[k<<]*mul[k])%p;
sum[k<<|]=(sum[k<<|]*mul[k])%p;
mul[k<<]=(mul[k<<]*mul[k])%p;
mul[k<<|]=(mul[k<<|]*mul[k])%p;
add[k<<]=(add[k<<]*mul[k])%p;//加法标记也要乘
add[k<<|]=(add[k<<|]*mul[k])%p;
mul[k]=;//清除标记
}
if(add[k])//当有加法标记
{
sum[k<<]=(sum[k<<]+add[k]*(mid-l+)%p)%p;//左右子树计算 右子树不用+1
sum[k<<|]=(sum[k<<|]+add[k]*(r-mid)%p)%p;//注意这里要先算sum 再算add
add[k<<]=(add[k]+add[k<<])%p;//如果先算add 那再sum中的add值已经改变
add[k<<|]=(add[k]+add[k<<|])%p;//坑了我一个晚上(太弱了)
add[k]=;//清除标记
}
return;
}
void update1(ll x,ll y,ll v,ll l,ll r,ll k)//在定区间[x,y]加上v
{
if(l>=x&&r<=y)//如果整个区间包含
{
add[k]=(add[k]+v)%p;
sum[k]=(sum[k]+(r-l+)*v)%p;
return;
}
ll mid=(l+r)>>;
pushdown(l,r,mid,k);
if(x<=mid) update1(x,y,v,l,mid,k<<);
if(mid<y) update1(x,y,v,mid+,r,k<<|);
sum[k]=(sum[k<<]+sum[k<<|])%p;//计算标记下放之后的值
return;
}
void update2(ll x,ll y,ll v,ll l,ll r,ll k)//在定区间[x,y]乘上v
{
if(l>=x&&r<=y)//如果整个区间包含
{
mul[k]=(mul[k]*v)%p;
add[k]=(add[k]*v)%p;
sum[k]=(sum[k]*v)%p;
return;
}
ll mid=(l+r)>>;
pushdown(l,r,mid,k);
if(x<=mid) update2(x,y,v,l,mid,k<<);
if(mid<y) update2(x,y,v,mid+,r,k<<|);
sum[k]=(sum[k<<]+sum[k<<|])%p;//计算标记下放之后的值
return;
}
ll query(ll x,ll y,ll l,ll r,ll k)
{
if(l>=x&&r<=y) return sum[k]%p;
ll mid=(l+r)>>;
ll res=;
pushdown(l,r,mid,k);//标记下放
if(x<=mid) res+=query(x,y,l,mid,k<<);
res%=p;
if(y>mid) res+=query(x,y,mid+,r,k<<|);
return res%p;
}
int main()
{
cin>>n>>p;
for(ll i=;i<=n;i++) cin>>a[i];
cin>>m;
build(,n,);
for(ll i=;i<=m;i++)
{
ll x,y,z;
cin>>x;
if(x==)
{
cin>>x>>y>>z;
update2(x,y,z,,n,);
}
else if(x==)
{
cin>>x>>y>>z;
update1(x,y,z,,n,);
}
else if(x==)
{
cin>>x>>y;
cout<<query(x,y,,n,)<<endl;
}
}
}
【题解】洛谷P2023 [AHOI2009] 维护序列(线段树)的更多相关文章
- 洛谷 P2023 [AHOI2009]维护序列 || 线段树加法和乘法运算
原理倒是非常简单.设原数为x,加法的lazytag为b,乘法的lazytag为a,操作数为c,那么原式为ax+b,乘上c后(ax+b)c=(ac)*x+b*c,加上c后(ax+b)+c=ax+(b+c ...
- 洛谷P2023 [AHOI2009]维护序列(线段树区间更新,区间查询)
洛谷P2023 [AHOI2009]维护序列 区间修改 当我们要修改一个区间时,要保证 \(ax+b\) 的形式,即先乘后加的形式.当将区间乘以一个数 \(k\) 时,原来的区间和为 \(ax+b\) ...
- 洛谷 P2023 [AHOI2009]维护序列 题解
P2023 [AHOI2009]维护序列 题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,-,aN .有如下三种操作形式: (1)把数列中 ...
- 洛谷 P2023 [AHOI2009]维护序列
P2023 [AHOI2009]维护序列 题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中 ...
- [洛谷P2023] [AHOI2009]维护序列
洛谷题目链接:[AHOI2009]维护序列 题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,-,aN .有如下三种操作形式: (1)把数列 ...
- [P2023][AHOI2009]维护序列(线段树)
题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一 ...
- 洛谷 2023 [AHOI2009]维护序列
洛谷 2023 [AHOI2009]维护序列 洛谷原题传送门 这个题也是一道经典的线段树模版(其实洛谷的模版二改一下输入顺序就能AC),其中包括区间乘法修改.区间加法修改.区间查询三个操作. 线段树的 ...
- BZOJ1798[Ahoi2009]维护序列——线段树
题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2 ...
- [AHOI2009]维护序列 (线段树)
题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,-,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一 ...
随机推荐
- 设计模式入门,命令模式,c++代码实现
// test06.cpp : Defines the entry point for the console application.////设计模式第5章 命令模式#include "s ...
- mongodb使用实践---mongodb+mongo-java-driver+morphia
package com.lolaage.dals.dbfactory.mongodb; import java.net.UnknownHostException; import java.util.A ...
- easyui 删除行的时候 引起的 bug
问题场景 easyui 编辑里 有删除行的功能 文档提供的方法如下 $aplgrid为grid 的容器 ind 为当前行的索引 $aplgrid.datagrid('deleteRow', ind); ...
- C++基础--class的大小
在这里列出了空类,类有函数,值,没有函数的大小,注意Class也是默认字节对齐 #include <stdio.h> class x { }; class cx { public: cx( ...
- 基于Vue的WebApp项目开发(二)
利用webpack解析和打包.vue组件页面 相关知识: vue项目中的每个页面其实都是一个.vue的文件,这种文件,Vue称之为组件页面,必须借助于webpack的vue-loader才能运行,所以 ...
- Django后台注册
- Linux->ZooKeeper开机启动的俩种方式
两种方式可以实现开机自启动 第一种:直接修改/etc/rc.d/rc.local文件 在/etc/rc.d/rc.local文件中需要输入两行, 其中export JAVA_HOME=/usr/jav ...
- git error:【fatal: unable to access 'https://github.com/userId/prjName.git/': err or setting certificate verify locations:】
$ git pull origin master fatal: unable to access 'https://github.com/userId/prjName.git/': err or se ...
- 设计模式:解释器(Interpreter)模式
设计模式:解释器(Interpreter)模式 一.前言 这是我们23个设计模式中最后一个设计模式了,大家或许也没想到吧,竟然是编译原理上的编译器,这样说可能不对,因为编译器分为几个部分组成呢,比如词 ...
- TCP、UDP、Socket 通信(原)
说明:本随笔主要演示自己给自己发送消息例子,分别使用了TCP协议.UDP协议以及socket套接字通信.使用socket套接字了模拟TCP.UDP通信实现原理.其中有些源码都来自<C#高级编程 ...