BZOJ 1798:

6:

LAZY 线段树有乘法的更新

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <iostream>

using namespace std;

const int maxn = 101000;

long long value[maxn], mod;

struct SegNode {

int left, right;

long long sum, add, mul;

int mid() {

return (left + right) >> 1;

}

int size() {

return right - left + 1;

}

};

struct SegmentTree {

SegNode tree[maxn*5];

void pushUp(int idx) {

tree[idx].sum = (tree[idx<<1].sum + tree[idx<<1|1].sum) % mod;

}

void pushDown(int idx) {

tree[idx<<1].add = (tree[idx].mul % mod * tree[idx<<1].add % mod + tree[idx].add) % mod;

tree[idx<<1|1].add = (tree[idx].mul % mod * tree[idx<<1|1].add % mod + tree[idx].add) % mod;

tree[idx<<1].mul = tree[idx<<1].mul % mod * tree[idx].mul % mod;

tree[idx<<1|1].mul = tree[idx<<1|1].mul % mod * tree[idx].mul % mod;

tree[idx<<1].sum = (tree[idx<<1].sum % mod * tree[idx].mul % mod

+ tree[idx<<1].size() * tree[idx].add % mod) % mod;

tree[idx<<1|1].sum = (tree[idx<<1|1].sum % mod * tree[idx].mul % mod

+ tree[idx<<1|1].size() * tree[idx].add % mod) % mod;

tree[idx].add = 0;

tree[idx].mul = 1;

}

void build(int left, int right, int idx) {

tree[idx].left = left;

tree[idx].right = right;

tree[idx].sum = 0;

tree[idx].mul = 1;

tree[idx].add = 0;

if (left == right) {

tree[idx].sum = value[left] % mod;

return ;

}

int mid = tree[idx].mid();

build(left, mid, idx<<1);

build(mid+1, right, idx<<1|1);

pushUp(idx);

}

void update(int left, int right, int idx, int opt, long long val) {

if (tree[idx].left == left && tree[idx].right == right) {

if (opt == 1) {

tree[idx].add = (tree[idx].add + val) % mod;

tree[idx].sum = (tree[idx].sum + tree[idx].size() % mod * val) % mod;

} else {

tree[idx].add = tree[idx].add % mod * val % mod;

tree[idx].mul = tree[idx].mul % mod * val % mod;

tree[idx].sum = tree[idx].sum % mod * val % mod;

}

return ;

}

pushDown(idx);

int mid = tree[idx].mid();

if (right <= mid)

update(left, right, idx<<1, opt, val);

else if (left > mid)

update(left, right, idx<<1|1, opt, val);

else {

update(left, mid, idx<<1, opt, val);

update(mid+1, right, idx<<1|1, opt, val);

}

pushUp(idx);

}

long long query(int left, int right, int idx) {

if (tree[idx].left == left && tree[idx].right == right) {

return tree[idx].sum % mod;

}

pushDown(idx);

int mid = tree[idx].mid();

if (right <= mid)

return query(left, right, idx<<1);

else if (left > mid)

return query(left, right, idx<<1|1);

else {

return (query(left, mid, idx<<1) % mod + query(mid+1, right, idx<<1|1));

}

}

};

SegmentTree tree;

int n, m;

void init() {

scanf("%d %lld", &n, &mod);

for (int i = 1; i <= n; i++)

scanf("%lld", &value[i]);

tree.build(1, n, 1);

}

时间: 2024-10-19 02:29:42

BZOJ 1798:的相关文章

bzoj 1798 维护序列seq

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1798 题解: 高级一点的线段树,加上了区间乘法运算,则需要增加一个数组mulv记录乘的因数,在下放更新sumv和addv值的都时候要先乘再加 被蓝书的写法坑了,就一直搞不懂下放和sumv.addv数组的具体用法,导致网上大犇们的程序我基本都看不懂,写完这道题感觉重新学了一遍线段树 1 #include<cstdio> 2 #include<cstring> 3 #defin

BZOJ 1798 题解

1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec  Memory Limit: 64 MBSubmit: 5531  Solved: 1946[Submit][Status][Discuss] Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和

bzoj 1798 [Ahoi2009]Seq 维护序列seq(线段树+传标)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1798 [题意] 给定一个序列,要求提供区间乘/加,以及区间求和的操作 [思路] 线段树+传标. 下传标记的方式可以类比这里 click here [代码] 1 #include<set> 2 #include<cmath> 3 #include<queue> 4 #include<vector> 5 #include<cstdio>

bzoj 1798 [Ahoi2009]Seq 维护序列seq

原题链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1798 线段树区间更新: 1. 区间同同时加上一个数 2. 区间同时乘以一个数 1 #include<algorithm> 2 #include<iostream> 3 #include<cstdlib> 4 #include<cstdio> 5 #define lc root<<1 6 #define rc root<<1|1

值得一做》关于双标记线段树两三事BZOJ 1798 (NORMAL-)

这是一道双标记线段树的题,很让人很好的预习/学习/复习线段树,我不知道它能让别人学习什么,反正让我对线段树的了解更加深刻. 题目没什么好讲的,程序也没什么好讲的,所以也没有什么题解,但是值得一做 给出题目&代码 Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需

BZOJ 1798: [Ahoi2009]Seq 维护序列seq (线段树乘法加法的混合操作)

 题目:点击打开链接 大意:一个数组,三个操作,第一种是区间[a,b]每个数乘乘,第二种是区间[a,b]每个数加c,第三种是查询[a,b]区间的和并对p取摸. 两种操作就不能简单的只往下传标记.每次传乘法标记时,要把加法标记同时乘上乘法标记,例如某个区间先进来一个加法标记add,之后又进来一个乘法标记mul. 那么结果为(x + add) * mul = x * mul + add * mul.这样向下传标记的时候就相对独立.递归边界更新加法标记之前先乘上该节点的mul,左右儿子down的时

bzoj 1798 双标记区间修改线段树

1 #include<bits/stdc++.h> 2 using namespace std; 3 #define MAXN 100000 4 #define M ((L+R)>>1) 5 #define lc (id<<1) 6 #define rc (id<<1|1) 7 #define LL long long 8 LL C[(MAXN<<2)+15]; 9 LL P,laz1[(MAXN<<2)+15],laz2[(MAXN

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

1798: [Ahoi2009]Seq 维护序列seq Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=1798 Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列

bzoj 1798 Seq 维护序列seq —— 线段树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1798 这题还4A... 注意:cnt 从1开始:各种模 p:乘法标记初始值是 1:可能乘 0. 代码如下: #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define mid ((l+r)>>1) using namespace std;

bzoj 1798 [Ahoi2009]Seq 维护序列seq ——线段树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1798 先乘后加,就可给加法标记乘上乘法标记. 注意可能有 *0 的操作,所以 pshd 时不是 cg[ cr ]>1 而是 cg[ cr ]!=1 . #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long #de