「线段树」[AHOI2009]维护序列
Posted nicoppa
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「线段树」[AHOI2009]维护序列相关的知识,希望对你有一定的参考价值。
双倍经验,还是蓝题,岂不美哉
题目描述
老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。 有长为N的数列,不妨设为\(a_1,a_2,…,a_N\) 。有如下三种操作形式:
(1)把数列中的一段数全部乘一个值;
(2)把数列中的一段数全部加一个值;
(3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值。
输入格式
第一行两个整数\(N\)和\(P\)\((1≤P≤1000000000)\)。
第二行含有\(N\)个非负整数,从左到右依次为\(a_1,a_2,…,a_N\), \((0≤a_i≤1000000000,1≤i≤N)\)。
第三行有一个整数\(M\),表示操作总数。
从第四行开始每行描述一个操作,输入的操作有以下三种形式:
操作1:“1 t g c”(不含双引号)。表示把所有满足\(t≤i≤g\)的\(a_i\)改为\(a_i×c\)\((1≤t≤g≤N,0≤c≤1000000000)\)。
操作2:“2 t g c”(不含双引号)。表示把所有满足\(t≤i≤g\)的\(a_i\)改为\(a_i+c\) \((1≤t≤g≤N,0≤c≤1000000000)\)。
操作3:“3 t g”(不含双引号)。询问所有满足\(t≤i≤g\)的\(a_i\)的和模P的值 \((1≤t≤g≤N)\)。
同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。
输出格式
对每个操作3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。
输入输出样例
输入
7 43
1 2 3 4 5 6 7
5
1 2 5 5
3 2 4
2 3 7 9
3 1 3
3 4 7
输出
2
35
8
说明/提示
【样例说明】
初始时数列为(1,2,3,4,5,6,7)。
经过第1次操作后,数列为(1,10,15,20,25,6,7)。
对第2次操作,和为10+15+20=45,模43的结果是2。
经过第3次操作后,数列为(1,10,24,29,34,15,16
对第4次操作,和为1+10+24=35,模43的结果是35。
对第5次操作,和为29+34+15+16=94,模43的结果是8。
测试数据规模如下表所示
数据编号 1 2 3 4 5 6 7 8 9 10
N= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000
M= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000
Source: Ahoi 2009
题目题解
还是三个操作,线段树基本题
//#define fre yes
#include <cstdio>
#define int long long
const int N = 100005;
struct Node
int l, r;
long long sum, addv, mul;
tree[N << 2];
int MOD;
long long ans;
void build(int k, int l, int r)
tree[k].l = l; tree[k].r = r; tree[k].addv = 0; tree[k].mul = 1;
if(l == r)
scanf("%lld", &tree[k].sum);
tree[k].sum %= MOD;
return ;
int mid = (l + r) >> 1;
build(k * 2, l, mid);
build(k * 2 + 1, mid + 1, r);
tree[k].sum = (tree[k * 2].sum + tree[k * 2 + 1].sum) % MOD;
void update(int k)
tree[k * 2].addv = (tree[k].mul * tree[k * 2].addv + tree[k].addv) % MOD;
tree[k * 2 + 1].addv = (tree[k].mul * tree[k * 2 + 1].addv + tree[k].addv) % MOD;
tree[k * 2].mul = (tree[k].mul * tree[k * 2].mul) % MOD;
tree[k * 2 + 1].mul = (tree[k].mul * tree[k * 2 + 1].mul) % MOD;
tree[k * 2].sum = (tree[k * 2].sum * tree[k].mul + (tree[k * 2].r - tree[k * 2].l + 1) * tree[k].addv) % MOD;
tree[k * 2 + 1].sum = (tree[k * 2 + 1].sum * tree[k].mul + (tree[k * 2 + 1].r - tree[k * 2 + 1].l + 1) * tree[k].addv) % MOD;
tree[k].addv = 0;
tree[k].mul = 1;
void mul_interval(int k, int l, int r, int x)
if(tree[k].l >= l && tree[k].r <= r)
tree[k].sum = (tree[k].sum * x) % MOD;
tree[k].addv = (tree[k].addv * x) % MOD;
tree[k].mul = (tree[k].mul * x) % MOD;
return ;
if(tree[k].addv || tree[k].mul != 1) update(k);
int mid = (tree[k].l + tree[k].r) >> 1;
if(mid >= l) mul_interval(k * 2, l, r, x);
if(mid < r) mul_interval(k * 2 + 1, l, r, x);
tree[k].sum = (tree[k * 2].sum + tree[k * 2 + 1].sum) % MOD;
void add_interval(int k, int l, int r, int x)
if(tree[k].l >= l && tree[k].r <= r)
tree[k].sum = (tree[k].sum + (tree[k].r - tree[k].l + 1) * x) % MOD;
tree[k].addv = (tree[k].addv + x) % MOD;
return ;
if(tree[k].addv || tree[k].mul != 1) update(k);
int mid = (tree[k].l + tree[k].r) >> 1;
if(mid >= l) add_interval(k * 2, l, r, x);
if(mid < r) add_interval(k * 2 + 1, l, r, x);
tree[k].sum = (tree[k * 2].sum + tree[k * 2 + 1].sum) % MOD;
void query(int k, int l, int r)
if(tree[k].l >= l && tree[k].r <= r)
ans = (ans + tree[k].sum) % MOD;
return ;
if(tree[k].addv || tree[k].mul != 1) update(k);
int mid = (tree[k].l + tree[k].r) >> 1;
if(mid >= l) query(k * 2, l, r);
if(mid < r) query(k * 2 + 1, l, r);
signed main()
static int n, m;
scanf("%lld %lld", &n, &MOD);
build(1, 1, n);
scanf("%lld", &m);
for (int i = 1; i <= m; i++)
int k;
scanf("%lld", &k);
if(k == 1)
int l, r, x;
scanf("%lld %lld %lld", &l, &r, &x);
mul_interval(1, l, r, x);
if(k == 2)
int l, r, x;
scanf("%lld %lld %lld", &l, &r, &x);
add_interval(1, l, r, x);
if(k == 3)
int l, r; ans = 0;
scanf("%lld %lld", &l, &r);
query(1, l, r);
printf("%lld\n", ans % MOD);
return 0;
以上是关于「线段树」[AHOI2009]维护序列的主要内容,如果未能解决你的问题,请参考以下文章
bzoj1798: [Ahoi2009]Seq 维护序列seq(线段树)
BZOJ1798: [Ahoi2009]Seq 维护序列seq[线段树]