题意

请你写一个数据结构,支持:

  1. 子序列同加
  2. 子序列同乘
  3. 统计子序列和

题目

线段树裸题,但对于我这种初学者还是非常难写。

我们维护两个标记,一个是在这个节点上作过的所有乘法操作,一个是加法操作,始终保持乘法优先级在前,这就说明,如果原来已经有了加法,那么我们需要把加法让位,即把加法×乘法,然后乘法搞一下原来的值。

对于如果一个操作与一个区间不完全覆盖,我们需要把lazy标记下传,对于下传操作,也需要按照上面的方法计算优先级,同时更新节点sum值。

注意在加法操作时,不要使用节点的add值更新sum,而要用val,因为add已经计算在sum里面了。

代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n, p;
ll m;
const int maxn = 100005;
struct seg {
int l, r;
ll sum, add, tag;
} t[4 * maxn];
void build(int k, int l, int r) {
t[k].l = l;
t[k].r = r;
if (r == l) {
t[k].tag = 1;
t[k].add = 0;
scanf("%lld", &t[k].sum);
t[k].sum %= p;
return;
}
int mid = (l + r) >> 1;
build(k << 1, l, mid);
build(k << 1 | 1, mid + 1, r);
t[k].sum = (t[k << 1].sum + t[k << 1 | 1].sum) % p;
t[k].add = 0;
t[k].tag = 1;
}
void pushdown(int k) {
if (t[k].add == 0 && t[k].tag == 1)
return;
ll ad = t[k].add, tag = t[k].tag;
ad %= p, tag %= p;
int l = t[k].l, r = t[k].r, mid = (l + r) >> 1;
t[k << 1].tag = (t[k << 1].tag * tag) % p;
t[k << 1].add = ((t[k << 1].add * tag) % p + ad) % p;
t[k << 1].sum = (((t[k << 1].sum * tag) % p + (ad * (mid - l + 1) % p)%p)%p) % p;
t[k << 1 | 1].tag = (t[k << 1 | 1].tag * tag) % p;
t[k << 1 | 1].add = ((t[k << 1 | 1].add * tag) % p + ad) % p;
t[k << 1 | 1].sum = (((t[k << 1|1].sum * tag) % p + (ad * (r-mid) % p)%p)%p) % p;
t[k].add = 0;
t[k].tag = 1;
return;
}
void update(int k) { t[k].sum = (t[k << 1].sum%p + t[k << 1 | 1].sum%p) % p; }
void add(int k, int x, int y, ll val) {
int l = t[k].l, r = t[k].r, mid = (l + r) >> 1;
if (x <= l && r <= y) {
t[k].add = (t[k].add + val) % p;
t[k].sum = (t[k].sum + (val * (r - l + 1) % p) % p) % p;
return;
}
pushdown(k);
if (x <= mid)
add(k << 1, x, y, val);
if (y > mid)
add(k << 1 | 1, x, y, val);
update(k);
}
void mul(int k, int x, int y, ll val) {
int l = t[k].l, r = t[k].r, mid = (l + r) >> 1;
if (x <= l && r <= y) {
t[k].add = (t[k].add * val) % p;
t[k].tag = (t[k].tag * val) % p;
t[k].sum = (t[k].sum * val) % p;
return;
}
pushdown(k);
if (x <= mid)
mul(k << 1, x, y, val);
if (y > mid)
mul(k << 1 | 1, x, y, val);
update(k);
}
ll query(int k, int x, int y) {
int l = t[k].l, r = t[k].r, mid = (l + r) >> 1;
if (x <= l && r <= y) {
return t[k].sum%p;
}
pushdown(k);
ll ans = 0;
if (x <= mid)
ans = (ans + query(k << 1, x, y)) % p;
if (y > mid)
ans = (ans + query(k << 1 | 1, x, y)) % p;
update(k);
return ans%p;
}
int main() {
// freopen("seq.in", "r", stdin);
// freopen("seq.ans", "w", stdout);
scanf("%lld %lld", &n, &p);
build(1, 1, n);
scanf("%d", &m);
while (m--) {
int q;
scanf("%d", &q);
if (q == 1) {
int x, y;
ll z;
scanf("%d%d%lld", &x, &y, &z);
z%=p;
mul(1, x, y, z);
} else if (q == 2) {
int x, y;
ll z;
scanf("%d%d%lld", &x, &y, &z);
z%=p;
add(1, x, y, z);
} else {
int x, y;
scanf("%d %d", &x, &y);
printf("%lld\n", query(1, x, y));
}
}
return 0;
}

[bzoj1798][Ahoi2009]Seq——线段树+多重标记下传的更多相关文章

  1. 【BZOJ】1798: [Ahoi2009]Seq 维护序列seq 线段树多标记(区间加+区间乘)

    [题意]给定序列,支持区间加和区间乘,查询区间和取模.n<=10^5. [算法]线段树 [题解]线段树多重标记要考虑标记与标记之间的相互影响. 对于sum*b+a,+c直接加上即可. *c后就是 ...

  2. BZOJ1798: [Ahoi2009]Seq 维护序列seq[线段树]

    1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec  Memory Limit: 64 MBSubmit: 5504  Solved: 1937[Submit ...

  3. bzoj 1798: [Ahoi2009]Seq 维护序列seq (线段树 ,多重标记下放)

    1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec  Memory Limit: 64 MBSubmit: 7773  Solved: 2792[Submit ...

  4. 【bzoj1798】[Ahoi2009]Seq 维护序列seq 线段树

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

  5. [bzoj1798][Ahoi2009]Seq 维护序列seq ([洛谷P3373]【模板】线段树 2)

    题目大意:有$n$个数,有$m$个操作,有三种: $1\;l\;r\;x:$把区间$[l,r]$内的数乘上$x$ $2\;l\;r\;x:$把区间$[l,r]$内的数加上$x$ $3\;l\;r:$询 ...

  6. [BZOJ1798][AHOI2009]Seq维护序列 线段树

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1798 一眼看过去线段树,事实上就是线段树.对于乘和加的两个标记,我们可以规定一个顺序,比如 ...

  7. BZOJ 1798: [Ahoi2009]Seq 维护序列seq( 线段树 )

    线段树.. 打个 mul , add 的标记就好了.. 这个速度好像还挺快的...( 相比我其他代码 = = ) 好像是#35.. ---------------------------------- ...

  8. bzoj 1798: [Ahoi2009]Seq 维护序列seq 线段树 区间乘法区间加法 区间求和

    1798: [Ahoi2009]Seq 维护序列seq Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeO ...

  9. Bzoj 1798: [Ahoi2009]Seq 维护序列seq(线段树区间操作)

    1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec Memory Limit: 64 MB Description 老师交给小可可一个维护数列的任务,现在小可 ...

随机推荐

  1. 【数据库】 SQLite 语法

    [数据库] SQLite 语法 一 . 创建数据库 1. 只需创建数据库,只需创建文件,操作时将连接字符串指向该文件即可 2. 连接字符串 : data source = FilePath; 不能加密 ...

  2. LaTeX工具——mathpix安利

    官网: https://mathpix.com/ 效果看下图: 图片打不开点这里 识别效果还行,感觉很适合jbc/zcy这种不喜欢打LaTex公式的神仙.

  3. jsp 中获取自定义变量

    首先确定El表达式的查找范围: 依次从Page.Request.Session.Application 中的 attribute属性中查找. <% String basePath = reque ...

  4. 「日常训练」Soldier and Badges (CFR304D2B)

    题意 (Codeforces 546B) 问对一个序列最少需要增减几个1能使其彼此不同. 分析 模拟处理.需要注意的是,尽管题目中说了an<=3000,问题是,如果一群a全是3000呢(滑稽), ...

  5. Java IO学习--File类

    一.File类 File类具备一定的误导性,可能容易认为它指代的是文件,实际并非如此,它既能代表一个特定文件的名称,又能表示一个目录下一组文件的名称.简而言之,File类是文件或者目录路径名的抽象表示 ...

  6. Android之ViewPager 第一课

    想要了解Android新版本的的新特性,从头开始吧,这是Android3.0新加入的widget,以前也接触过,但是没有好好的研究过,今天写了一个小程序,研究一下ViewPager. 这个程序是支持左 ...

  7. eclipse快捷键(复制自己看)

    1几个最重要的快捷键 代码助手:Ctrl+Space(简体中文操作系统是Alt+/)快速修正:Ctrl+1单词补全:Alt+/打开外部Java文档:Shift+F2 显示搜索对话框:Ctrl+H快速O ...

  8. 【转】给大家分享一下目前mlc颗粒的内存卡资料

    以下信息是LZ从其它论坛上找到的TF卡也是有讲究的,一分价钱一分货 dboy99 楼主 骚(6) #1楼 2015-8-5 14:49引用Micro SD卡也叫TF卡,作为手机扩展存储空间的唯一方式用 ...

  9. 基于Thinkphp5+phpQuery 网络爬虫抓取数据接口,统一输出接口数据api

    TP5_Splider 一个基于Thinkphp5+phpQuery 网络爬虫抓取数据接口 统一输出接口数据api.适合正在学习Vue,AngularJs框架学习 开发demo,需要接口并保证接口不跨 ...

  10. 关于如何利用原生js动态给一个空对象添加属性以及属性值

    首先,回忆一下,访问对象属性一共有两种方法:点获取法和方括号获取法.而我们最常用的就是点获取法了.但是当我们遇到需要给对象动态添加属性和属性值时,点获取法好像就不太好用了,尤其是我们不知道属性名的时候 ...