洛谷 2023 [AHOI2009]维护序列

洛谷原题传送门

这个题也是一道经典的线段树模版(其实洛谷的模版二改一下输入顺序就能AC),其中包括区间乘法修改、区间加法修改、区间查询三个操作。

 线段树的基本操作就不再赘述了,建树,查询,修改,都比较简单,我们可以为两种操作的懒惰标记申请两个变量来记录,这道题的主要难点是down操作和两种修改的优先级问题。

 这两个问题其实就是一回事:首先当我们下推标记时,如果该点加法标记不为0且乘法标记不为1(乘法标记初始化为1),那我们应该先推哪个标记呢?

 实际上我们应该先推位置靠前(也就是比较早输入)的运算,可是运用懒惰标记后,我们是难以判断那个运算输入的更早。

 有一种解决方法就是在每次乘法修改时,将对应位置的加法懒惰标记乘以修改值。这样我们保证了每次下推标记时必然是乘法优先。

if(tree[u].l>=x&&tree[u].r<=y)
{
tree[u].w*=c;
tree[u].w%=mod;
tree[u].mu=(tree[u].mu*c)%mod;
tree[u].ad=(tree[u].ad*c)%mod;
return;
}

这里的模运算的性质:

((a*b) % p * c)% p = (a * (b*c) % p) % p

(a * b) % p = (b * a) % p

我们可以随时对程序里的标记和值做模运算。

而下推标记时,我们只要保证乘法优先,就可以轻松打出代码:

void down(int u)
{
tree[u<<].w=(tree[u<<].w*tree[u].mu%mod+tree[u].ad*(tree[u<<].r-tree[u<<].l+)%mod)%mod;
tree[u<<|].w=(tree[u<<|].w*tree[u].mu%mod+tree[u].ad*(tree[u<<|].r-tree[u<<|].l+)%mod)%mod;
tree[u<<].mu=tree[u].mu*tree[u<<].mu%mod;
tree[u<<|].mu=tree[u].mu*tree[u<<|].mu%mod;
tree[u<<].ad=(tree[u].ad+tree[u<<].ad*tree[u].mu)%mod; //下推时加法标记也要做乘法运算
tree[u<<|].ad=(tree[u].ad+tree[u<<|].ad*tree[u].mu)%mod;
tree[u].ad=; //清空标记
tree[u].mu=;
}

下附完整AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=;
typedef long long ll; struct segtree{
int l;
int r;
ll w;
ll ad;
ll mu;
}tree[MAXN<<]; int n,m,q,x,y; ll mod,c,ans; ll read()
{
ll x=;
int k=;
char c=getchar();
while(c>''||c<'')
{
if(c=='-') k=-;
c=getchar();
}
while(c<=''&&c>='')
x=x*+c-'',
c=getchar();
return k*x;
} void build(int u,int l,int r)
{
tree[u].l=l;
tree[u].r=r;
tree[u].ad=;
tree[u].mu=;
if(l==r)
{
tree[u].w=read()%mod;
return;
}
int mid=(l+r)>>;
build(u<<,l,mid);
build(u<<|,mid+,r);
tree[u].w=(tree[u<<].w+tree[u<<|].w)%mod;
return;
} void down(int u)
{
tree[u<<].w=(tree[u<<].w*tree[u].mu%mod+tree[u].ad*(tree[u<<].r-tree[u<<].l+)%mod)%mod;
tree[u<<|].w=(tree[u<<|].w*tree[u].mu%mod+tree[u].ad*(tree[u<<|].r-tree[u<<|].l+)%mod)%mod;
tree[u<<].mu=tree[u].mu*tree[u<<].mu%mod;
tree[u<<|].mu=tree[u].mu*tree[u<<|].mu%mod;
tree[u<<].ad=(tree[u].ad+tree[u<<].ad*tree[u].mu)%mod;
tree[u<<|].ad=(tree[u].ad+tree[u<<|].ad*tree[u].mu)%mod;
tree[u].ad=;
tree[u].mu=;
} void query(int u)
{
if(tree[u].l>=x&&tree[u].r<=y)
{
ans=(ans+tree[u].w)%mod;
return;
}
down(u);
int mid=(tree[u].l+tree[u].r)>>;
if(x<=mid) query(u<<);
if(y>mid) query(u<<|);
return;
} void mul(int u)
{
if(tree[u].l>=x&&tree[u].r<=y)
{
tree[u].w*=c;
tree[u].w%=mod;
tree[u].mu=(tree[u].mu*c)%mod;
tree[u].ad=(tree[u].ad*c)%mod;
return;
}
down(u);
int mid=(tree[u].l+tree[u].r)>>;
if(x<=mid) mul(u<<);
if(y>mid) mul(u<<|);
tree[u].w=(tree[u<<].w+tree[u<<|].w)%mod;
return;
} void add(int u)
{
if(tree[u].l>=x&&tree[u].r<=y)
{
tree[u].ad=(tree[u].ad+c)%mod;
tree[u].w+=(tree[u].r-tree[u].l+)*c;
tree[u].w%=mod;
return;
}
down(u);
int mid=(tree[u].l+tree[u].r)>>;
if(x<=mid) add(u<<);
if(y>mid) add(u<<|);
tree[u].w=(tree[u<<].w+tree[u<<|].w)%mod;
return;
} int main()
{
n=read();
mod=read();
build(,,n);
m=read();
for(int i=;i<=m;++i)
{
q=read();
x=read();
y=read();
if(q==)
{
c=read();
mul();
}
if(q==)
{
c=read();
add();
}
if(q==)
{
ans=;
query();
printf("%lld\n",ans%mod);
}
}
   return 0;
}

洛谷 2023 [AHOI2009]维护序列的更多相关文章

  1. 洛谷P2023 [AHOI2009]维护序列(线段树区间更新,区间查询)

    洛谷P2023 [AHOI2009]维护序列 区间修改 当我们要修改一个区间时,要保证 \(ax+b\) 的形式,即先乘后加的形式.当将区间乘以一个数 \(k\) 时,原来的区间和为 \(ax+b\) ...

  2. [洛谷P2023] [AHOI2009]维护序列

    洛谷题目链接:[AHOI2009]维护序列 题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,-,aN .有如下三种操作形式: (1)把数列 ...

  3. 洛谷 P2023 [AHOI2009]维护序列

    P2023 [AHOI2009]维护序列 题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中 ...

  4. 洛谷 P2023 [AHOI2009]维护序列 题解

    P2023 [AHOI2009]维护序列 题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,-,aN .有如下三种操作形式: (1)把数列中 ...

  5. 【题解】洛谷P2023 [AHOI2009] 维护序列(线段树)

    洛谷P2023:https://www.luogu.org/problemnew/show/P2023 思路 需要2个Lazy-Tag 一个表示加的 一个表示乘的 需要先计算乘法 再计算加法 来自你谷 ...

  6. 洛谷 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 ...

  7. [Luogu 2023] AHOI2009 维护序列

    [Luogu 2023] AHOI2009 维护序列 恕我冒昧这和线段树模板二有个琴梨区别? #include <cstdio> int n,m; long long p; class S ...

  8. 洛谷P3373 【模板】线段树 2 && P2023 [AHOI2009]维护序列——题解

    题目传送: P3373 [模板]线段树 2  P2023 [AHOI2009]维护序列 该题较传统线段树模板相比多了一个区间乘的操作.一提到线段树的区间维护问题,就自然想到了“懒标记”:为了降低时间复 ...

  9. BZOJ_1798_[AHOI2009]维护序列_线段树

    BZOJ_1798_[AHOI2009]维护序列_线段树 题意:老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: ( ...

随机推荐

  1. P1291-添加括号(区间dp)

    题目背景 给定一个正整数序列a(1),a(2),...,a(n),(1<=n<=20) 不改变序列中每个元素在序列中的位置,把它们相加,并用括号记每次加法所得的和,称为中间和. 例如: 给 ...

  2. Docker安装nginx以及负载均衡

    首先在linux系统中新建一个data文件夹进行nginx容器的创建--即为:mkdir data. 一:第一次 1 第一步: 使用 docker pull nginx将nginx的镜像从仓库下载下来 ...

  3. Mac终端操作移动硬盘文件命令

    桌面上看到的硬盘都挂载在 /Volumes目录下 例如移动硬盘名为ZTB,有System目录,则应输入命令: cd /Volumes/ZTB/System 注意:进入Volumes目录命令为cd /V ...

  4. 删除vi编辑产生的.swp文件(linux编辑文件没有退出时直接关闭程序产生的临时文件)

    关于swp文件 使用vi,经常可以看到swp这个文件,那这个文件是怎么产生的呢,当你打开一个文件,vi就会生成这么一个.(filename)swp文件以备不测(不测下面讨论),如果你正常退出,那么这个 ...

  5. openSUSE 跨版本升级

    准备工作 此方法通过网络跨版本升级,适合 Leap 升级到下一个发行版(如 42.2 升级到 42.3),也适合 Leap 升级为 Tumbleweed.首先必须确定升级的时候有足够的时间.靠谱的更新 ...

  6. Mac客户端CentOS服务器 SSH免密码登陆

    假定有2个服务器A(127.0.0.1)和B(192.168.0.1),A作为客户端来登录服务器B 1.在服务器A下使用 ssh-keygen -t ras -P 会在-/.ssh目录下'生成公钥(i ...

  7. Netty(1-1)Discard

    一.DiscardServerHandler import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext ...

  8. Net Core2.0 基于QuartzNet任务管理系统

    Net Core2.0 基于QuartzNet任务管理系统 Quartz.NET官网地址:https://www.quartz-scheduler.net/ Quartz.NET文档地址:https: ...

  9. spring中自动装配bean

    首先用@Component注解类: package soundsystem: import org.springframework.stereotype.Component; @Component p ...

  10. Random类、ThreadLocalRandom类

    Random和ThreadLocalRandom类均用于生成伪随机数. Random的构造函数: Random()     默认以系统当前时间为种子,相当于Random(System.currentT ...