题目链接
P3374 【模板】树状数组 1
P3368 【模板】树状数组 2
P3372 【模板】线段树 1
P3373 【模板】线段树 2
P6242 【模板】线段树 3
P3374 【模板】树状数组 1
题目描述
如题,已知一个数列,你需要进行下面两种操作:
输入格式
第一行包含两个正整数 n , m n , m ,分别表示该数列数字的个数和操作的总个数。
第二行包含 n n 个用空格分隔的整数,其中第 i i 个数字表示数列第 i i 项的初始值。
接下来 m m 行每行包含 3 3 个整数,表示一个操作,具体如下:
输出格式
输出包含若干行整数,即为所有操作 2 2 的结果。
输入
输出
说明/提示
【数据范围】
对于 30 % 30 % 的数据,1 ≤ n ≤ 8 , 1 ≤ m ≤ 10 1 ≤ n ≤ 8 , 1 ≤ m ≤ 10 ;
对于 70 % 70 % 的数据,1 ≤ n , m ≤ 10 4 1 ≤ n , m ≤ 10 4 ;
对于 100 % 100 % 的数据,1 ≤ n , m ≤ 5 × 10 5 1 ≤ n , m ≤ 5 × 10 5 。
代码
P3368 【模板】树状数组 2
题目描述
如题,已知一个数列,你需要进行下面两种操作:
输入格式
第一行包含两个整数 N 、 M N 、 M ,分别表示该数列数字的个数和操作的总个数。
第二行包含 N N 个用空格分隔的整数,其中第 i i 个数字表示数列第 i i 项的初始值。
接下来 M M 行每行包含 2 2 或 4 4 个整数,表示一个操作,具体如下:
操作 1 1 : 格式:1 x y k
含义:将区间 [ x , y ] [ x , y ] 内每个数加上 k k ;
操作 2 2 : 格式:2 x
含义:输出第 x x 个数的值。
输出格式
输出包含若干行整数,即为所有操作 2 2 的结果。
输入
输出
说明/提示
数据规模与约定
对于 30 % 30 % 的数据:N ≤ 8 , M ≤ 10 N ≤ 8 , M ≤ 10 ;
对于 70 % 70 % 的数据:N ≤ 10000 , M ≤ 10000 N ≤ 10000 , M ≤ 10000 ;
对于 100 % 100 % 的数据:1 ≤ N , M ≤ 500000 , 1 ≤ x , y ≤ n 1 ≤ N , M ≤ 500000 , 1 ≤ x , y ≤ n ,保证任意时刻序列中任意元素的绝对值都不大于 2 30 2 30 。
代码
P3372 【模板】线段树 1
题目描述
如题,已知一个数列,你需要进行下面两种操作:
将某区间每一个数加上 k k 。
求出某区间每一个数的和。
输入格式
第一行包含两个整数 n , m n , m ,分别表示该数列数字的个数和操作的总个数。
第二行包含 n n 个用空格分隔的整数,其中第 i i 个数字表示数列第 i i 项的初始值。
接下来 m m 行每行包含 3 3 或 4 4 个整数,表示一个操作,具体如下:
1 x y k
:将区间 [ x , y ] [ x , y ] 内每个数加上 k k 。
2 x y
:输出区间 [ x , y ] [ x , y ] 内每个数的和。
输出格式
输出包含若干行整数,即为所有操作 2 2 的结果。
输入
输出
说明/提示
对于 30 % 30 % 的数据:n ≤ 8 , m ≤ 10 n ≤ 8 , m ≤ 10 。
对于 70 % 70 % 的数据:n ≤ 10 3 , m ≤ 10 4 n ≤ 10 3 , m ≤ 10 4 。
对于 100 % 100 % 的数据:1 ≤ n , m ≤ 10 5 1 ≤ n , m ≤ 10 5 。
保证任意时刻数列中任意元素的和在 [ u − 2 63 , 2 63 ) [ u − 2 63 , 2 63 ) 内。
树状数组
解法
我们不妨用树状数组维护前缀和,∑ x i = 1 c [ i ] ∑ i = 1 x c [ i ] 就是 a [ x ] a [ x ] 增加的值,而序列 a a 的前缀和 a [ 1 ∼ x ] a [ 1 ∼ x ] 增加的值为:
x ∑ i = 1 i ∑ j = 1 c [ j ] ⇔ x ∑ i = 1 ( x − i + 1 ) × c [ i ] ⇔ ( x + 1 ) x ∑ i = 1 c [ i ] − x ∑ i = 1 i × c [ i ] ∑ i = 1 x ∑ j = 1 i c [ j ] ⇔ ∑ i = 1 x ( x − i + 1 ) × c [ i ] ⇔ ( x + 1 ) ∑ i = 1 x c [ i ] − ∑ i = 1 x i × c [ i ]
代码
线段树(懒标记)
代码
#include<bits/stdc++.h>
using namespace std ;
const int N=1e5 +10 ;
using LL=long long ;
struct seg
{
int l,r;
LL sum,add ;
#define l(x) tr[x].l
#define r(x) tr[x].r
#define sum(x) tr[x].sum
#define add(x) tr[x].add
}tr[N*4 ];
int n,m;
int a[N];
void build (int p,int l,int r )
{
l(p)=l,r(p)=r;
if (l==r)sum(p)=a[l];
else
{
int mid=l+r>>1 ;
build(p*2 ,l,mid),build(p*2 +1 ,mid+1 ,r);
sum(p)=sum(p*2 )+sum(p*2 +1 );
}
}
void spread (int p )
{
if (add (p))
{
sum(p*2 )+=add (p)*(r(p*2 )-l(p*2 )+1 );
sum(p*2 +1 )+=add (p)*(r(p*2 +1 )-l(p*2 +1 )+1 );
add (p*2 )+=add (p);
add (p*2 +1 )+=add (p);
add (p)=0 ;
}
}
void change (int p,int l,int r,int d )
{
if (l<=l(p)&&r(p)<=r)
{
sum(p)+=1l l*d*(r(p)-l(p)+1 );
add (p)+=d;
return ;
}
spread(p);
int mid=l(p)+r(p)>>1 ;
if (l<=mid)change(p*2 ,l,r,d);
if (r>mid)change(p*2 +1 ,l,r,d);
sum(p)=sum(p*2 )+sum(p*2 +1 );
}
LL ask (int p,int l,int r )
{
if (l<=l(p)&&r(p)<=r)
return sum(p);
spread(p);
LL res=0 ;
int mid=l(p)+r(p)>>1 ;
if (l<=mid)res+=ask(p*2 ,l,r);
if (r>mid)res+=ask(p*2 +1 ,l,r);
return res;
}
int main ()
{
scanf("%d%d" ,&n,&m);
for (int i=1 ;i<=n;i++)
scanf("%d" ,&a[i]);
build(1 ,1 ,n);
while (m--)
{
int op,x,y;
LL k;
scanf("%d%d%d" ,&op,&x,&y);
if (op==1 )
{
scanf("%lld" ,&k);
change(1 ,x,y,k);
}
else
printf("%lld\n" ,ask(1 ,x,y));
}
return 0 ;
}
P3373 【模板】线段树 2
题目描述
如题,已知一个数列,你需要进行下面三种操作:
将某区间每一个数乘上 x x
将某区间每一个数加上 x x
求出某区间每一个数的和
输入格式
第一行包含三个整数 n , m , p n , m , p ,分别表示该数列数字的个数、操作的总个数和模数。
第二行包含 n n 个用空格分隔的整数,其中第 i i 个数字表示数列第 i i 项的初始值。
接下来 m m 行每行包含若干个整数,表示一个操作,具体如下:
操作 1 1 : 格式:1 x y k
含义:将区间 [ x , y ] [ x , y ] 内每个数乘上 k k
操作 2 2 : 格式:2 x y k
含义:将区间 [ x , y ] [ x , y ] 内每个数加上 k k
操作 3 3 : 格式:3 x y
含义:输出区间 [ x , y ] [ x , y ] 内每个数的和对 p p 取模所得的结果
输出格式
输出包含若干行整数,即为所有操作 3 3 的结果。
输入
输出
说明/提示
【数据范围】
对于 30 % 30 % 的数据:n ≤ 8 , m ≤ 10 n ≤ 8 , m ≤ 10
对于 70 % 70 % 的数据:n ≤ 10 3 , m ≤ 10 4 n ≤ 10 3 , m ≤ 10 4
对于 100 % 100 % 的数据:n ≤ 10 5 , m ≤ 10 5 n ≤ 10 5 , m ≤ 10 5
除样例外,p = 571373 p = 571373
解题思路
本题的关键在于加法和乘法的顺序问题:
加法优先级低,不会对前面的操作有影响
乘法优先级高,对前面的操作有影响,有三种影响:s u m s u m 、懒标记 m u l m u l 和 a d d a d d 。
所以,采取先乘后加的方式
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5 +10 ;
using LL=long long ;
struct seg
{
int l,r;
LL sum,add,mul;
#define l(x) tr[x].l
#define r(x) tr[x].r
#define add(x) tr[x].add
#define mul(x) tr[x].mul
#define sum(x) tr[x].sum
}tr[N*4 ];
int n,m,mod;
int a[N];
void build (int p,int l,int r)
{
l (p)=l,r (p)=r;
mul (p)=1 ;
if (l==r)
{
sum (p)=a[l]%mod;
return ;
}
int mid=l+r>>1 ;
build (p*2 ,l,mid),build (p*2 +1 ,mid+1 ,r);
sum (p)=(sum (p*2 )+sum (p*2 +1 ))%mod;
}
void spread (int p)
{
sum (p*2 )=(sum (p*2 )*mul (p)+add (p)*(r (p*2 )-l (p*2 )+1 ))%mod;
sum (p*2 +1 )=(sum (p*2 +1 )*mul (p)+add (p)*(r (p*2 +1 )-l (p*2 +1 )+1 ))%mod;
add (p*2 )=(add (p*2 )*mul (p)+add (p))%mod;
add (p*2 +1 )=(add (p*2 +1 )*mul (p)+add (p))%mod;
mul (p*2 )=mul (p*2 )*mul (p)%mod;
mul (p*2 +1 )=mul (p*2 +1 )*mul (p)%mod;
add (p)=0 ;
mul (p)=1 ;
}
void change1 (int p,int l,int r,int d)
{
if (l<=l (p)&&r (p)<=r)
{
add (p)=add (p)*d%mod;
mul (p)=mul (p)*d%mod;
sum (p)=(sum (p)*d)%mod;
return ;
}
spread (p);
int mid=l (p)+r (p)>>1 ;
if (l<=mid)change1 (p*2 ,l,r,d);
if (r>mid)change1 (p*2 +1 ,l,r,d);
sum (p)=(sum (p*2 )+sum (p*2 +1 ))%mod;
}
void change2 (int p,int l,int r,int d)
{
if (l<=l (p)&&r (p)<=r)
{
add (p)=(add (p)+d)%mod;
sum (p)=(sum (p)+(r (p)-l (p)+1 )*d)%mod;
return ;
}
spread (p);
int mid=l (p)+r (p)>>1 ;
if (l<=mid)change2 (p*2 ,l,r,d);
if (r>mid)change2 (p*2 +1 ,l,r,d);
sum (p)=(sum (p*2 )+sum (p*2 +1 ))%mod;
}
int ask (int p,int l,int r)
{
if (l<=l (p)&&r (p)<=r)return sum (p);
spread (p);
int res=0 ;
int mid=l (p)+r (p)>>1 ;
if (l<=mid)res+=ask (p*2 ,l,r)%mod;
if (r>mid)res+=ask (p*2 +1 ,l,r);
return res%mod;
}
int main ()
{
scanf ("%d%d%d" ,&n,&m,&mod);
for (int i=1 ;i<=n;i++)scanf ("%d" ,&a[i]);
build (1 ,1 ,n);
while (m--)
{
int op,x,y,k;
scanf ("%d%d%d" ,&op,&x,&y);
if (op==1 )
{
scanf ("%d" ,&k);
change1 (1 ,x,y,k);
}
else if (op==2 )
{
scanf ("%d" ,&k);
change2 (1 ,x,y,k);
}
else
printf ("%d\n" ,ask (1 ,x,y));
}
return 0 ;
}
P6242 【模板】线段树 3
题目描述
给出一个长度为 n n 的数列 A A ,同时定义一个辅助数组 B B ,B B 开始与 A A 完全相同。接下来进行了 m m 次操作,操作有五种类型,按以下格式给出:
1 l r k
:对于所有的 i ∈ [ l , r ] i ∈ [ l , r ] ],将 A i A i 加上 k k (k k 可以为负数)。
2 l r v
:对于所有的 i ∈ [ l , r ] i ∈ [ l , r ] ,将 A i A i 变成 min ( A i , v ) min ( A i , v ) 。
3 l r
:求 ∑ r i = l A i ∑ i = l r A i 。
4 l r
:对于所有的 i ∈ [ l , r ] i ∈ [ l , r ] ,求 A i A i 的最大值。
5 l r
:对于所有的 i ∈ [ l , r ] i ∈ [ l , r ] ,求 B i B i 的最大值。
在每一次操作后,我们都进行一次更新,让 B i ← max ( B i , A i ) B i ← max ( B i , A i ) 。
输入格式
第一行包含两个正整数 n , m n , m ,分别表示数列 A A 的长度和操作次数。
第二行包含 n n 个整数 A 1 , A 2 , ⋯ , A n A 1 , A 2 , ⋯ , A n ,表示数列 A A 。
接下来 m m 行,每行行首有一个整数 o p o p ,表示操作类型;接下来两个或三个整数表示操作参数,格式见【题目描述】。
输出格式
对于 o p ∈ { 3 , 4 , 5 } o p ∈ { 3 , 4 , 5 } 的操作,输出一行包含一个整数,表示这个询问的答案。
输入
输出
解题思路
代码
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!