洛谷 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. NOIp2013 火柴排队【逆序对/思维】 By cellur925

    题目大意:给你两列数\(ai\)和\(bi\),你可以交换每列数中相邻的两个数,求一个最小交换次数使\(\sum_{i=1}^{n}(a_i-b_i)^2\) 最小. 最后满足条件的两个序列一定是各个 ...

  2. JVM 内存分析

    简述JVM垃圾回收机制 垃圾回收机制时Java提供的自动释放内存空间的机制. 垃圾回收机制时JVM自导的一个线程,用于回收没有被引用的对象. JVM有一个运行时的数据区来管理内存.其主要包括五大部分: ...

  3. Django之缓存+序列化+信号+ORM性能优化+验证码

    缓存 由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加 明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcach ...

  4. opencv 形态学膨胀和腐蚀以及开运算和闭运算

  5. 定时器 & 日期时间对象 & 正则

    1 JavaScript 计时事件 通过使用 JavaScript,有能力做到在一个设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行,这称之为计时事件. 两个关键方法是: setInterv ...

  6. 百度地图API的基本用法

    首先 ,如果想调用百度地图api,你需要获取一个百度地图api的密钥. 申请秘钥的步骤: 1.搜索百度地图: 2.进入后,先登录然后点击申请密钥: 3. 4.申请成功,拥有密钥 有了密钥之后,引入百度 ...

  7. reactnative资源

    http://facebook.github.io/react-native/docs/getting-started.html

  8. selenium+python之自动换测试用例执行

    1.一个用例为一个完整的场景,从用户登陆系统到最终退出并关闭浏览器. 2.一个用例只验证一个功能点,不要试图在用户登陆系统后把所有的功能都验证一遍. 3.尽可能少的编写逆向逻辑用例.一方面因为逆向逻辑 ...

  9. 【Python图像特征的音乐序列生成】GitHub已经有人将mingus改到了Python3版本

    https://github.com/bspaans/python-mingus/issues/45 注意此时的安装方法应该是: git clone https://github.com/edudob ...

  10. java面试题(杨晓峰)---以面试题为切入点,有效提升你的java内功

    java是一门历史悠久的编程语言,可以毫无争议的说,java是最主流的编程语言之一.全球有1200万以上的java程序猿以及海量的设备,还有无所不能的java生态圈. 我所知道的诸如阿里,京东,百度, ...