线段树板子
线段树最原始的功能是区间求和
因为是树状结构,使得查询和修改的复杂度都为O(log n)级别
又因为有懒标记的存在,大大降低了复杂度
所以我觉得,树状结构式线段树的基本
懒标记是线段树的灵魂
#include<iostream>
using namespace std;
#define ll long long
#define lid (id << 1)
#define rid (id << 1) | 1//位运算可以降低常数
const ll maxn = 100110;
ll a[maxn];
struct sag_tree{ll l,r;ll lazy;ll sum;}tree[maxn << 2];
void build(ll id,ll l,ll r){
tree[id].l = l;
tree[id].r = r;
if(l == r){
tree[id].sum = a[l];
return;//记得return
}
ll mid = (l + r) >> 1;
build(lid,l,mid);
build(rid,mid + 1,r);
tree[id].sum = tree[lid].sum + tree[rid].sum;
}
void pushdown(ll id){//懒标记下放(pushdown(i)是更新儿子而不是自己)
if(tree[id].lazy != 0 && tree[id].l != tree[id].r){
ll val = tree[id].lazy;
tree[lid].lazy += val;
tree[rid].lazy += val;
tree[lid].sum += val * (tree[lid].r - tree[lid].l + 1);
tree[rid].sum += val * (tree[rid].r - tree[rid].l + 1);
tree[id].lazy = 0;//记得归零
}
}
void update(ll id,ll val,ll l,ll r){
pushdown(id);
if(tree[id].l == l && tree[id].r == r){
tree[id].lazy += val;
tree[id].sum += val * (r - l + 1);
return;
}
ll mid = (tree[id].l + tree[id].r) >> 1;
if(mid < l){
update(rid,val,l,r);
}
else if(mid >= r){
update(lid,val,l,r);
}
else{
update(lid,val,l,mid);
update(rid,val,mid + 1,r);
}
tree[id].sum = tree[lid].sum + tree[rid].sum;
}
ll getsum(ll id,ll l,ll r){
pushdown(id);
if(tree[id].l == l && tree[id].r == r){
return tree[id].sum;
}
ll mid = (tree[id].l + tree[id].r) >> 1;
if(mid < l){
return getsum(rid,l,r);
}
else if(mid >= r){
return getsum(lid,l,r);
}
else{
return getsum(lid,l,mid) + getsum(rid,mid + 1,r);
}
}
int main(){
ll num,nask;
cin>>num>>nask;
for(ll i = 1;i <= num;i++)cin>>a[i];
build(1,1,num);
ll ask,x,y,v;
for(ll i = 1;i <= nask;i++){
cin>>ask;
if(ask == 1){
cin>>x>>y>>v;
update(1,v,x,y);
}
if(ask == 2){
cin>>x>>y;
cout<<getsum(1,x,y)<<endl;
}
}
return 0;
}
还有就是既涉及乘法又涉及加法是要有一个优先级的概念
#include<iostream>
#include<cstdio>
using namespace std;
long long RD(){
int out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
#define lid (id << 1)
#define rid (id << 1) | 1
#define ll long long
const long long maxn = 100100;
long long num,nask,P;
long long a[maxn];
struct sag_tree{long long l,r;long long sum,add,mul;}tree[maxn << 2];
void build(long long id,long long l,long long r){
tree[id].add = 0;
tree[id].mul = 1;
tree[id].l = l;
tree[id].r = r;
if(l == r){
tree[id].sum = a[l];
return ;
}
long long mid = (l + r) >> 1;
build(lid,l,mid);
build(rid,mid + 1,r);
tree[id].sum = (tree[lid].sum + tree[rid].sum) % P;
}
void pushdown(long long id){
if((tree[id].add != 0 || tree[id].mul != 1) && tree[id].l != tree[id].r){
long long add = tree[id].add;
long long mul = tree[id].mul;
tree[lid].sum *= mul;//乘法
tree[lid].sum %= P;
tree[lid].sum += (tree[lid].r - tree[lid].l + 1) * add;//加法
tree[lid].sum %= P;
tree[rid].sum *= mul;
tree[rid].sum %= P;
tree[rid].sum += (tree[rid].r - tree[rid].l + 1) * add;
tree[rid].sum %= P;//更新左右儿子
tree[lid].mul *= mul;
tree[lid].mul %= P;
tree[lid].add = (tree[lid].add * mul + tree[id].add) % P;
tree[rid].mul *= mul;
tree[rid].mul %= P;
tree[rid].add = (tree[rid].add * mul + tree[id].add) % P;
tree[id].add = 0;
tree[id].mul = 1;
}
}
void update1(long long id,long long val,long long l,long long r){
pushdown(id);
if(tree[id].l == l && tree[id].r == r){
tree[id].sum *= val;
tree[id].sum %= P;
tree[id].mul *= val;
tree[id].mul %= P;
tree[id].add *= val;
tree[id].add %= P;
return ;
}
long long mid = (tree[id].l + tree[id].r) >> 1;
if(mid < l){
update1(rid,val,l,r);
}
else if(mid >= r){
update1(lid,val,l,r);
}
else{
update1(lid,val,l,mid);
update1(rid,val,mid + 1,r);
}
tree[id].sum = (tree[lid].sum + tree[rid].sum) % P;
}
void update2(long long id,long long val,long long l,long long r){
pushdown(id);
if(tree[id].l == l && tree[id].r == r){
tree[id].sum += (tree[id].r - tree[id].l + 1) * val;
tree[id].sum %= P;
tree[id].add += val;
tree[id].add %= P;
return ;
}
long long mid = (tree[id].l + tree[id].r) >> 1;
if(mid < l){
update2(rid,val,l,r);
}
else if(mid >= r){
update2(lid,val,l,r);
}
else{
update2(lid,val,l,mid);
update2(rid,val,mid + 1,r);
}
tree[id].sum = (tree[lid].sum + tree[rid].sum) % P;
}
long long query(long long id,long long l,long long r){
pushdown(id);
if(tree[id].l == l && tree[id].r == r){
return tree[id].sum;
}
long long mid = (tree[id].l + tree[id].r) >> 1;
if(mid < l){
return query(rid,l,r);
}
else if(mid >= r){
return query(lid,l,r);
}
else{
return (query(lid,l,mid) + query(rid,mid + 1,r)) % P;
}
}
int main(){
num = RD();nask = RD();P = RD();
for(long long i = 1;i <= num;i++)a[i] = RD();
build(1,1,num);
long long ask,l,r,val;
for(long long i = 1;i <= nask;i++){
ask = RD();l = RD();r = RD();
if(ask == 1){
val = RD();
update1(1,val,l,r);
}
else if(ask == 2){
val = RD();
update2(1,val,l,r);
}
else{
printf("%d
",query(1,l,r));
}
}
return 0;
}
线段树的其他用法
我们可以人为的定义线段树的概念【在建树里得到体现】,对修改及查询函数做适当的修改,从而解决很多问题
很基础的例子就是查询一个区间内的最值:
P1531 I Hate It
题目背景
很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。这让很多学生很反感。
题目描述
不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩
输入输出格式
输入格式:
第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目。学生ID编号分别从1编到N。第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。接下来有M行。每一行有一个字符 C (只取'Q'或'U') ,和两个正整数A,B。当C为'Q'的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。当C为'U'的时候,表示这是一条更新操作,如果当前A学生的成绩低于B,则把ID为A的学生的成绩更改为B,否则不改动。
输出格式:
对于每一次询问操作,在一行里面输出最高成绩
典型的最大值查询问题。因为涉及分数修改,所以我们用线段树解决问题
我们定义一个线段树的max表示其管理区间内的最大值,建树时不更新为和,而是更新为最大值(修改定义)
对于更新函数,我们在修改后回溯时同样返回区间最大值
对于询问函数,若刚好为这个区间,则直接返回这个区间的max,若横跨区间,则找出这几个区间内的max,返回
#include<iostream>
#include<algorithm>
using namespace std;
#define lid (id << 1)
#define rid (id << 1) | 1
const int maxn = 200100;
int num,na;
int a[maxn];
struct sag_tree{int l,r,sum;}tree[maxn << 2];
void build(int id,int l,int r){
tree[id].l = l;
tree[id].r = r;
if(l == r){
tree[id].sum = a[l];
return;
}
int mid = (l + r) >> 1;
build(lid,l,mid);
build(rid,mid + 1,r);
tree[id].sum = max(tree[lid].sum,tree[rid].sum);//修改定义
}
void update(int id,int p,int l,int r){
if(tree[id].l == l && tree[id].r == r){
tree[id].sum = max(tree[id].sum,p);
return;
}
int mid = (tree[id].l + tree[id].r) >> 1;
if(mid < l){
update(rid,p,l,r);
}
else if(mid >= r){
update(lid,p,l,r);
}
tree[id].sum = max(tree[lid].sum,tree[rid].sum);//回溯维护区间最大值
}
int query(int id,int l,int r){
if(tree[id].l == l && tree[id].r == r){
return tree[id].sum;
}
int mid = (tree[id].l + tree[id].r) >> 1;
if(mid < l){
return query(rid,l,r);
}
else if(mid >= r){
return query(lid,l,r);
}
else{
return max(query(lid,l,mid),query(rid,mid + 1,r));
}
}
int main(){
cin>>num>>na;
for(int i = 1;i <= num;i++)cin>>a[i];
build(1,1,num);
char ask;
int a,b;
for(int i = 1;i <= na;i++){
cin>>ask>>a>>b;
if(ask == 'Q'){
cout<<query(1,a,b)<<endl;
}
else{
update(1,b,a,a);
}
}
return 0;
}
P2826 [USACO08NOV]光开关Light Switching
题目描述
灯是由高科技——外星人鼠标操控的。你只要左击两个灯所连的鼠标,
这两个灯,以及之间的灯都会由暗变亮,或由亮变暗。右击两个灯所连的鼠
标,你就可以知道这两个灯,以及之间的灯有多少灯是亮的。起初所有灯都是暗的,你的任务是在LZ之前算出灯的亮灭。
输入输出格式
输入格式:
第1 行: 用空格隔开的两个整数N 和M,n 是灯数
第2..M+1 行: 每行表示一个操作, 有三个用空格分开的整数: 指令号, S_i 和E_i
第1 种指令(用0 表示)包含两个数字S_i 和E_i (1 <= S_i <= E_i <= N), 它们表示起
始开关和终止开关. 表示左击
第2 种指令(用1 表示)同样包含两个数字S_i 和E_i (1 <= S_i <= E_i <= N), 不过这
种指令是询问从S_i 到E_i 之间的灯有多少是亮着的.
同样的
我们定义一个on,on == 1为亮;on == 0为暗
区间修改:区间内 1 -> 0 或 0 -> 1
查询:直接求和即可
懒标记下放:(因为题意,这题是可以标记下放的,所以)我们规定lazy == 1 表示儿子需要转换状态,lazy == 0 表示不需要,若更新时有lazy == 1,直接改为0即可
#include<iostream>
using namespace std;
#define ll long long
#define lid (id << 1)
#define rid (id << 1) | 1
const ll maxn = 100110;
ll a[maxn];
struct sag_tree{ll l,r;ll lazy;ll sum;}tree[maxn << 2];
void build(ll id,ll l,ll r){
tree[id].l = l;
tree[id].r = r;
if(l == r){
tree[id].sum = a[l];
return;
}
ll mid = (l + r) >> 1;
build(lid,l,mid);
build(rid,mid + 1,r);
tree[id].sum = tree[lid].sum + tree[rid].sum;
}
void pushdown(ll id){
if(tree[id].lazy == 1 && tree[id].l != tree[id].r){
tree[lid].lazy = 1 - tree[lid].lazy;
tree[rid].lazy = 1 - tree[rid].lazy;
tree[lid].sum = (tree[lid].r - tree[lid].l + 1) - tree[lid].sum;
tree[rid].sum = (tree[rid].r - tree[rid].l + 1) - tree[rid].sum;
tree[id].lazy = 0;
}
}
void update(ll id,ll l,ll r){
pushdown(id);
if(tree[id].l == l && tree[id].r == r){
tree[id].lazy = 1 - tree[id].lazy;
tree[id].sum = (tree[id].r - tree[id].l + 1) - tree[id].sum;
return;
}
ll mid = (tree[id].l + tree[id].r) >> 1;
if(mid < l){
update(rid,l,r);
}
else if(mid >= r){
update(lid,l,r);
}
else{
update(lid,l,mid);
update(rid,mid + 1,r);
}
tree[id].sum = tree[lid].sum + tree[rid].sum;
}
ll getsum(ll id,ll l,ll r){
pushdown(id);
if(tree[id].l == l && tree[id].r == r){
return tree[id].sum;
}
ll mid = (tree[id].l + tree[id].r) >> 1;
if(mid < l){
return getsum(rid,l,r);
}
else if(mid >= r){
return getsum(lid,l,r);
}
else{
return getsum(lid,l,mid) + getsum(rid,mid + 1,r);
}
}
int main(){
ll num,nask;
cin>>num>>nask;
build(1,1,num);
ll ask,x,y,v;
for(ll i = 1;i <= nask;i++){
cin>>ask;
if(ask == 0){
cin>>x>>y;
update(1,x,y);
}
if(ask == 1){
cin>>x>>y;
cout<<getsum(1,x,y)<<endl;
}
}
return 0;
}