YbtOJ 「数据结构」 第4章 线段树
线段树
维护区间的一棵树 四个基本操作:单点查询 单点修改 区间查询 区间修改
父节点维护的是子节点的值合并后的总和(区间加和 区间最大值等等)
每次在递归下去的时候 先判断区间 再下传标记 再递归寻找下一个区间
注意两个儿子是p<<1和p<<1|1 两个区间是[l,mid]和[mid+1,r]
一条链上只可能有一个节点拥有懒标记
如果修改区间覆盖了整个范围,就只需要打一个懒标记就好,打懒标记的同时需要更新这个被打标记的点的各项数值
等下一次问询到这个节点的时候,p节点的值已经更新好了,只需要把懒标记下传下去,子节点更新就可以了
A. 【例题1】求区间和
不需要打懒标记的单点修改区间查询 代码略
B. 【例题2】区间查改
代码略 详见洛谷【模板】线段树 1
C. 【例题3】公园遛狗
需要维护给定区间范围内的 一段连续小区间的区间和最大值
经典操作:设置
特别注意
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 4e6 + 5;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , m , a[N];
struct node { int sum , prel , prer , res; } t[N];
void up ( int p )
{
t[p].sum = t[ls].sum + t[rs].sum;
t[p].prel = max ( t[ls].sum + t[rs].prel , t[ls].prel );
t[p].prer = max ( t[rs].sum + t[ls].prer , t[rs].prer );
t[p].res = max ( max ( t[ls].res , t[rs].res ) , t[ls].prer + t[rs].prel );
}
void build ( int p , int l , int r )
{
if ( l == r ) return t[p] = { a[l] , a[l] , a[l] , a[l] } , void();
build ( lson ) , build ( rson ) , up(p);
}
void upd ( int p , int l , int r , int x , int val )
{
if ( l == r ) return t[p] = { val , val , val , val } , void();
if ( x <= mid ) upd ( lson , x , val );
else upd ( rson , x , val );
up(p);
}
node query ( int p , int l , int r , int x , int y )
{
if ( x <= l && r <= y ) return t[p];
if ( x >= mid + 1 ) return query ( rson , x , y );
else if ( y <= mid ) return query ( lson , x , y );
else
{
node temp , u = query ( lson , x , y ) , v = query ( rson , x , y );
temp.sum = u.sum + v.sum;
temp.prel = max ( u.sum + v.prel , u.prel );
temp.prer = max ( v.sum + u.prer , v.prer );
temp.res = max ( max ( u.res , v.res ) , u.prer + v.prel );
return temp;
}
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
build ( 1 , 1 , n );
for ( int i = 1 ; i <= m ; i ++ )
{
int op = read() , x = read() , y = read();
if ( op == 1 )
{
if ( x > y ) swap ( x , y );
cout << query ( 1 , 1 , n , x , y ).res << endl;
}
else upd ( 1 , 1 , n , x , y );
}
return 0;
}
D. 【例题4】维护序列
洛谷线段树2模板
上传的时候子节点的
注意:建树时无论是否是
懒标记下传时 低级别懒标记需要累加高级别懒标记的影响
赋值懒标记时 为高级别懒标记赋值的时候需要同时积累低级别懒标记的值
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 4e6 + 5
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , m , mod , a[N];
struct node { int sum , mul , add; } t[N];
void up ( int p ) { t[p].sum = ( t[ls].sum + t[rs].sum ) % mod; }
void down ( int p , int l , int r )
{
t[ls].sum = ( t[ls].sum * t[p].mul + ( mid - l + 1 ) * t[p].add ) % mod;
t[rs].sum = ( t[rs].sum * t[p].mul + ( r - mid ) * t[p].add ) % mod;
t[ls].mul = ( t[ls].mul * t[p].mul ) % mod;
t[rs].mul = ( t[rs].mul * t[p].mul ) % mod;
t[ls].add = ( t[ls].add * t[p].mul + t[p].add ) % mod;
t[rs].add = ( t[rs].add * t[p].mul + t[p].add ) % mod;
t[p].mul = 1 , t[p].add = 0;
}
void build ( int p , int l , int r )
{
t[p].mul = 1;
if ( l == r ) return t[p] = { a[l] , 1 , 0 } , void();
build ( lson ) , build ( rson ) , up(p);
}
void mul ( int p , int l , int r , int x , int y , int val )
{
if ( x <= l && r <= y ) return t[p].sum = ( t[p].sum * val ) % mod , t[p].mul = ( t[p].mul * val ) % mod , t[p].add = ( t[p].add * val ) % mod , void();
down ( p , l , r );
if ( x <= mid ) mul ( lson , x , y , val );
if ( mid + 1 <= y ) mul ( rson , x , y , val );
up(p);
}
void add ( int p , int l , int r , int x , int y , int val )
{
if ( x <= l && r <= y ) return t[p].sum = ( t[p].sum + ( r - l + 1 ) * val % mod ) % mod , t[p].add = ( t[p].add + val ) % mod , void();
down ( p , l , r );
if ( x <= mid ) add ( lson , x , y , val );
if ( mid + 1 <= y ) add ( rson , x , y , val );
up(p);
}
int query ( int p , int l , int r , int x , int y )
{
if ( x <= l && r <= y ) return t[p].sum;
down ( p , l , r );
int res = 0;
if ( x <= mid ) res = ( res + query ( lson , x , y ) % mod ) % mod;
if ( mid + 1 <= y ) res = ( res + query ( rson , x , y ) % mod ) % mod;
return res;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , mod = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
build ( 1 , 1 , n ) , m = read();
for ( int i = 1 ; i <= m ; i ++ )
{
int op = read() , l = read() , r = read() , val;
if ( op == 1 ) val = read() , mul ( 1 , 1 , n , l , r , val );
if ( op == 2 ) val = read() , add ( 1 , 1 , n , l , r , val );
if ( op == 3 ) cout << query ( 1 , 1 , n , l , r ) << endl;
}
return 0;
}
E. 【例题5】字符串排序
我们设置第
排序时等价于在区间
注意需要特判
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 1e6 + 5;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , m , mod , a[N] , tmp[N];
string s;
int t[N][27] , lazy[N];
void up ( int p ) { for ( int i = 1 ; i <= 26 ; i ++ ) t[p][i] = t[ls][i] + t[rs][i]; }
void changedown ( int p , int l , int r , int val )
{
lazy[p] = val;
for ( int i = 1 ; i <= 26 ; i ++ ) t[p][i] = 0;
t[p][val] = r - l + 1;
}
void down ( int p , int l , int r )
{
if ( lazy[p] )
{
changedown ( lson , lazy[p] );
changedown ( rson , lazy[p] );
lazy[p] = 0;
}
}
char aaa;
void build ( int p , int l , int r )
{
if ( l == r ) return t[p][s[l]-'a'+1] = 1 , void();
build ( lson ) , build ( rson ) , up(p);
}
int query ( int id , int p , int l , int r , int x , int y )
{
if ( x <= l && r <= y ) return t[p][id];
down ( p , l , r );
int ans = 0;
if ( x <= mid ) ans += query ( id , lson , x , y );
if ( mid + 1 <= y ) ans += query ( id , rson , x , y );
return ans;
}
void change ( int id , int p , int l , int r , int x , int y )
{
if ( x > y ) return;//需要特判
if ( x <= l && r <= y ) return changedown ( p , l , r , id ) , void();
down ( p , l , r );
if ( x <= mid ) change ( id , lson , x , y );
if ( mid + 1 <= y ) change ( id , rson , x , y );
up(p);
}
void print ( int p , int l , int r )
{
if ( l == r )
{
for ( int i = 1 ; i <= 26 ; i ++ )
if ( t[p][i] ) cout << (char)(i+'a'-1);
return;
}
down ( p , l , r ) , print ( lson ) , print ( rson );
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
cin >> s , s = " " + s;
build ( 1 , 1 , n );
for ( int i = 1 ; i <= m ; i ++ )
{
int l = read() , r = read() , op = read();
for ( int j = 1 ; j <= 26 ; j ++ ) tmp[j] = query ( j , 1 , 1 , n , l , r );
if ( op )
for ( int j = 1 ; j <= 26 ; j ++ )
{
change ( j , 1 , 1 , n , l , l + tmp[j] - 1 );
l += tmp[j];
if ( l > r ) continue;
}
else
for ( int j = 26 ; j; j -- )
{
change ( j , 1 , 1 , n , l , l + tmp[j] - 1 );
l += tmp[j];
if ( l > r ) continue;
}
}
print ( 1 , 1 , n );
return 0;
}
F. 1.取模问题
不需要懒标记 如果模数大于这段区间的最大值 那么直接跳过 其他和线段树维护一样
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 1e6 + 5;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , m , a[N];
struct node { int val , maxx; } t[N];
void up ( int p ) { t[p].maxx = max ( t[ls].maxx , t[rs].maxx ) , t[p].val = t[ls].val + t[rs].val; }
void build ( int p , int l , int r )
{
if ( l == r ) return t[p] = { a[l] , a[l] } , void();
build ( lson ) , build ( rson ) , up(p);
}
void upd ( int p , int l , int r , int x , int val )
{
if ( l == r ) return t[p] = { val , val } , void();
if ( x <= mid ) upd ( lson , x , val );
else upd ( rson , x , val );
up(p);
}
void mod ( int p , int l , int r , int x , int y , int val )
{
if ( val > t[p].maxx ) return;
if ( l == r ) return t[p].val %= val , t[p].maxx = t[p].val , void();
if ( x <= mid ) mod ( lson , x , y , val );
if ( mid + 1 <= y ) mod ( rson , x , y , val );
up(p);
}
int query ( int p , int l , int r , int x , int y )
{
if ( x <= l && r <= y ) return t[p].val;
int res = 0;
if ( x <= mid ) res += query ( lson , x , y );
if ( mid + 1 <= y ) res += query ( rson , x , y );
return res;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
build ( 1 , 1 , n );
for ( int i = 1 , val ; i <= m ; i ++ )
{
int op = read() , x = read() , y = read();
if ( op == 1 ) cout << query ( 1 , 1 , n , x , y ) << endl;
if ( op == 2 ) val = read() , mod ( 1 , 1 , n , x , y , val );
if ( op == 3 ) upd ( 1 , 1 , n , x , y );
}
return 0;
}
G. 2.魔法传输
我们维护一个差分数组 对于一个操作
对于查询 则查询该点前缀和即可
注意调用下传函数
需要特判
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 1e6 + 5;
const int mod = 1000000007;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , m , a[N];
struct node { int sum , add; } t[N];
void up ( int p ) { t[p].sum = ( t[ls].sum + t[rs].sum ) % mod; }
void changedown ( int p , int l , int r , int val )
{
t[p].sum = ( t[p].sum + ( r - l + 1 ) * val ) % mod;
t[p].add = ( t[p].add + val ) % mod;
}
void down ( int p , int l , int r )
{
if ( t[p].add )
{
changedown ( lson , t[p].add );
changedown ( rson , t[p].add );
t[p].add = 0;
}
}
void upd ( int p , int l , int r , int x , int y , int val )
{
if ( x <= l && r <= y ) return changedown ( p , l , r , val ) , void();
down ( p , l , r );
if ( x <= mid ) upd ( lson , x , y , val );
if ( mid + 1 <= y ) upd ( rson , x , y , val );
up(p);
}
int query ( int p , int l , int r , int x , int y )
{
if ( x <= l && r <= y ) return t[p].sum;
down ( p , l , r );
int res = 0;
if ( x <= mid ) res = ( res + query ( lson , x , y ) ) % mod;
if ( mid + 1 <= y ) res = ( res + query ( rson , x , y ) ) % mod;
return res;
}
char op;
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
for ( int i = 1 , x , y ; i <= m ; i ++ )
{
cin >> op;
if ( op == 'C' ) x = read() , y = read() , upd ( 1 , 1 , n , x , y , 1 ) , upd ( 1 , 1 , n , y + 1 , y + 1 , - ( y - x + 1 ) );
if ( op == 'Q' ) x = read() , cout << query ( 1 , 1 , n , 1 , x ) << endl;
}
return 0;
}
H. 3.队伍整理
对于
统计的时候标记所有有值的位置并标记 统计vis数组的前缀和 在
注意坑点:查询的时候上限是
需要特判
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 2e5 + 5;
const int inf = 1e9;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , m , flag , a[N] , b[10000010] , vis[N] , sum[N] , ans , t[N<<2];
void up ( int p ) { t[p] = min ( t[ls] , t[rs] ); }
void build ( int p , int l , int r )
{
if ( l == r ) return t[p] = a[l] , void();
build ( lson ) , build ( rson ) , up(p);
}
void upd ( int p , int l , int r , int x , int val )
{
if ( l == r ) return t[p] = val , void();
if ( x <= mid ) upd ( lson , x , val );
else upd ( rson , x , val );
up(p);
}
int query ( int p , int l , int r , int x , int y )
{
if ( y < x ) return inf;
if ( x <= l && r <= y ) return t[p];
int res = inf;
if ( x <= mid ) res = min ( res , query ( lson , x , y ) );
if ( mid + 1 <= y ) res = min ( res , query ( rson , x , y ) );
return res;
}
char op;
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
for ( int i = 1 ; i <= m + n ; i ++ ) a[i] = inf;
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , b[a[i]] = i;
build ( 1 , 1 , n + m );
flag = n;
for ( int i = 1 , x ; i <= m ; i ++ )
{
cin >> op , x = read();
if ( op == 'A' )
{
if ( !b[x] ) { cout << -1 << endl; continue; }
int temp = query ( 1 , 1 , n + m , 1 , b[x] - 1 );
if ( temp == inf ) temp = -1;
cout << temp << endl;
}
else
{
upd ( 1 , 1 , n + m , b[x] , inf );
b[x] = ++flag;
upd ( 1 , 1 , n + m , b[x] , x );
}
}
for ( int i = 1 ; i <= n ; i ++ ) vis[b[a[i]]] = 1;
for ( int i = 1 ; i <= n + m ; i ++ ) sum[i] = sum[i-1] + vis[i];
for ( int i = n ; i <= n + m ; i ++ ) ans = max ( ans , sum[i] - sum[i-n] );
cout << n - ans << endl;
return 0;
}
I. 4.和或异或
考虑用线段树直接模拟这
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 4e5 + 5;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , q , t[N<<2] , st[N] , a[N];//st为1是子节点或上来 st为0是异或上来
void up ( int p )
{
st[p] = st[ls] ^ 1;
if ( !st[p] ) t[p] = t[ls] ^ t[rs];
else t[p] = t[ls] | t[rs];
}
void build ( int p , int l , int r )
{
if ( l == r ) return t[p] = a[l] , void();
build ( lson ) , build ( rson ) , up(p);
}
void upd ( int p , int l , int r , int x , int val )
{
if ( l == r ) return t[p] = val , void();
if ( x <= mid ) upd ( lson , x , val );
else upd ( rson , x , val );
up(p);
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , q = read();
for ( int i = 1 ; i <= 1 << n ; i ++ ) a[i] = read();
build ( 1 , 1 , 1 << n );
for ( int i = 1 ; i <= q ; i ++ )
{
int x = read() , y = read();
upd ( 1 , 1 , 1 << n , x , y );
cout << t[1] << endl;
}
return 0;
}
J. 5.括号匹配
我们设置
三个转移更新见代码 注意结构体需要初始化
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 4e5 + 5;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , m;
string s;
struct node { int nol , nor , res; } t[N<<2];//区间内有多少个不能匹配的左括号/右括号 整个区间匹配括号对数
node merge ( node x , node y )
{
node res = { 0 , 0 , 0 };
res.nol = x.nol + y.nol - min ( x.nol , y.nor );
res.nor = x.nor + y.nor - min ( x.nol , y.nor );
res.res = x.res + y.res + min ( x.nol , y.nor );
return res;
}
void up ( int p ) { t[p] = merge ( t[ls] , t[rs] ); }
void build ( int p , int l , int r )
{
if ( l == r ) return s[l] == '(' ? t[p].nol = 1 : t[p].nor = 1 , void();
build ( lson ) , build ( rson ) , up(p);
}
node query ( int p , int l , int r , int x , int y )
{
if ( x <= l && r <= y ) return t[p];
node ans = { 0 , 0 , 0 };
if ( x <= mid ) ans = merge ( ans , query ( lson , x , y ) );
if ( mid + 1 <= y ) ans = merge ( ans , query ( rson , x , y ) );
return ans;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
cin >> s; s = " " + s;
build ( 1 , 1 , n );
for ( int i = 1 ; i <= m ; i ++ )
{
int x = read() , y = read();
node temp = query ( 1 , 1 , n , x , y );
cout << temp.res * 2 << endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!