树状数组
实际应用
//树状数组 (前缀和、区间、点更新)
const int N=10005;
int n,a[N],b[N];//树状数组b
int lowbit(int i)
{
return (-i)&i;
}
void add(int i,int z)//点更新,a[i]+z
{
for(i<=n;i+=lowbit(i))//更新所有后继
{
b[i]+=z;
}
}
int sum(int i)//前缀和 ,a[1]...a[i]
{
int s=0;
for(;i>0;i-=lowbit(i))//累加所有前驱
s+=c[i];
return s;
}
线段树
求某区间最值或更新点
/线段树(区间更新)开4n区间
/*创建
1.若叶子节点,节点的最值即对应元素值
2.非叶子节点,递归创建左子树和右子树
3.节点区间的最值==该节点左右子树最值的最大值
*/
#define lc k<<1//左孩子存储下标
#define rc k<<1+1// 右孩子存储下标
const int N=10005;
const int inf=0x3f3f3f3f;
int n,a[N];
struct node{
int l,r,mx;
}tree[N*4];
void build(int k,int l,int r)//创建线段树
{
tree[k].l=l;
tree[k].r=r;
if(l==r){//叶子节点
tree[k].mx=a[l];
return ;
}
int mid=(l+r)/2;
build(lc,l,mid);//左孩子
build(rc,mid+1,r);//右孩子
//更新最值
tree[k].mx=max(tree[lc].mx,tree[rc].mx); //回归更新
}
void updata(int k,int i,int v)//点更新
{
if(tree[k].l==tree[k].r&&tree[k].l==i)//叶子节点
{tree[k].mx=v
return ;}
int mid=(tree[k].l+tree[k].r)/2;
if(i<=mid)//左子树
updata(lc,i,v);
else
updata(rc,i,v);
//回归更新最值
tree[k].mx=max(tree[lc].mx,tree[rc].mx);
}
//查询一个区间的最值
/*若节点所在的区间被【l,r】覆盖,则返回该节点最值
2.判断是在左子树还是右子树
3.返回最值*/
int query1(int k,int l,int r)
{
if(tree[k].l>=l&&tree[k].r<=r)//区间被覆盖
return tree[k].mx ;
int mid=(tree[k].l+tree[k].r)/2;
int M=-inf;//负无穷 //必须局部
if(l<=mid)//左区间查找
M=max(M,query(lc,l,r))//左子树查询
if(r>=mid+1)
M=max(M,query(rc,l,r)) //右子树找
return M;
}
//区间缩小
int query2(int k,int l,int r)
{
if(tree[k].l==l&&tree[k].r==r)//区间相等
return tree[k].mx;
int mid=(tree[k].l+tree[k].r)/2;
int M=-inf;//负无穷 //必须局部
if(r<=mid)//区间全在左区间
return query(lc,l,r);//左子树查询
else if(l>=mid+1)//区间全在右区间
return query(rc,l,r) ;//右子树找
else {//跨区间
return max(query2(lc,l,mid),query2(rc,mid+1,r));
}
}
例题
I Hate ItProblem Detail | 2022ICPC 程序设计竞赛基础第九周周赛 (jxnu.edu.cn)
描述:
很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。
这让很多学生很反感。
不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。
输入:
本题目包含多组测试,请处理到文件结束。
在每个测试的第一行,有两个正整数 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'的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。
输出:
对于每一次询问操作,在一行里面输出最高成绩。
样例输入:
5 6 1 2 3 4 5 Q 1 5 U 3 6 Q 3 4 Q 4 5 U 2 9 Q 1 5
复制
样例输出:
5 6 5 9
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=200002;
int s[N];
const int inf=0x3f3f3f3f;
struct node{
int l,r,mx;
}tree[4*N];
void build(int k,int l,int r)
{
tree[k].l=l,tree[k].r=r;
if(l==r)
{
tree[k].mx=s[l];
return ;
}
int mid=(l+r) /2;
build(k*2,l,mid);
build(k*2+1,mid+1,r);
tree[k].mx=max(tree[k*2].mx,tree[(k*2)+1].mx);
}
void updata(int k,int i,int v)
{
if(tree[k].l==tree[k].r)
{
tree[k].mx=v;
return ;
}
int mid=(tree[k].l+tree[k].r)/2;
if(i<=mid)
updata(k*2,i,v);
else if(i>=mid+1)
updata((k*2)+1,i,v);
tree[k].mx=max(tree[k*2].mx,tree[(k*2)+1].mx);
}
int query(int k,int l,int r)
{
if(tree[k].l>=l&&tree[k].r<=r)
return tree[k].mx;
int M=-inf;
int mid=(tree[k].l+tree[k].r)/2;
if(l<=mid)
M=max(M,query(k*2,l,r));
if(r>=mid+1)
M=max(M,query((k*2)+1,l,r));
return M;
}
/*
int query(int k,int l,int r)
{
if(tree[k].l==l&&tree[k].r==r)
return tree[k].mx;
int mid=(tree[k].l+tree[k].r)/2;
if(r<=mid)
return query(k*2,l,r);
else if(l>mid)
return query(k*2+1,l,r);
else return max(query(k*2,l,mid),query(k*2+1,mid+1,r));
}*/
int main()
{
int n,m;
while(scanf("%d %d",&n,&m)!=EOF)
{
for(int i=1;i<=n;i++)
scanf("%d",&s[i]);
build(1,1,n);
int a,b;
char c;
while(m--)
{ getchar();
scanf("%c %d %d",&c,&a,&b);
if(c=='Q')
printf("%d\n",query(1,a,b));
else
updata(1,a,b);
}
}
return 0;
}
区间查询与区间更新
点修改
/*线段树的区间查询与更新
*/
struct node{
int l,r,mx;
}tree[N];
int a[N];
#define lc k<<1
#define rc k<<1|1
void build(int k,int l,int r)//建立树
{
tree[k].l=l,tree[k].r=r;
if(l==r)
{
tree[k].mx=a[l];
return ;
}
int mid=(l+r)/2;
build(rc,mid+1,r);
build(lc,l,mid);
tree[k].mx=tree[rc].mx+tree[lc].mx;
}
void updata(int k,int i,int v)
{
if(tree[k].l==tree[k].r)
{
tree[k].mx=v;
a[l]=v;
return ;
}
int mid=(tree[k].l+tree[k].r)/2;
if(i>mid) updata(rc,i,v);
else updata(lc,i,v);
tree[k].mx=tree[rc].mx+tree[lc].mx;
}
int query(int k,int l,int r)
{
if(r<tree[k].l||tree[k].r<l)//所求区间不在该节点所维护的区间内
return 0;
if(tree[k].l==tree[k].r)
return tree[k].mx;//为叶子节点
int mid=(tree[k].l+tree[k].r)/2;
int suml=query(lc,l,mid);
int sumr=query(rc,mid+1,r);
return suml+sumr;
}
区间修改
例题
3243:A Simple Problem with Integers
总时间限制:
5000ms
单个测试点时间限制:
2000ms
内存限制:
131072kB
描述
You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.
输入
The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of Aa, Aa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of Aa, Aa+1, ... , Ab.
输出
You need to answer all Q commands in order. One answer in a line.
样例输入
10 5 1 2 3 4 5 6 7 8 9 10 Q 4 4 Q 1 10 Q 2 4 C 3 6 3 Q 2 4
样例输出
4 55 9 15
OpenJudge - 3243:A Simple Problem with Integers
#include<iostream>
#include<cstdio>
using namespace std;
const int N=100002;
typedef long long in;
in a[N];
struct node{
in l,r,mx,lazy;//懒惰标记
}tree[N*4];
#define rc k<<1|1
#define lc k<<1
void build(in k,in l,in r)
{
tree[k].l=l,tree[k].r=r,tree[k].lazy=0;
if(l==r)
{
tree[k].mx=a[l];
return ;
}
in mid=(l+r)/2;
build(lc,l,mid);
build(rc,mid+1,r);
tree[k].mx=tree[rc].mx+tree[lc].mx;
}
void down(in k)
{
tree[lc].lazy+=tree[k].lazy;
tree[rc].lazy+=tree[k].lazy;
tree[lc].mx+=tree[k].lazy*(tree[lc].r-tree[lc].l+1);
tree[rc].mx+=tree[k].lazy*(tree[rc].r-tree[rc].l+1);
tree[k].lazy=0;
}
void updata(in k,in l,in r,in v)
{
if(tree[k].l>=l&&tree[k].r<=r)
{
tree[k].mx+=(tree[k].r-tree[k].l+1)*v;
tree[k].lazy+=v;//到达一个区域,不用到达叶子结点
return ;
}
if(tree[k].lazy)down(k);
in mid=(tree[k].l+tree[k].r)/2;
if(r>mid) updata(rc,l,r,v);
if(l<=mid)updata(lc,l,r,v);
tree[k].mx=tree[rc].mx+tree[lc].mx;
}
in query(in k,in l,in r)
{
if(tree[k].l>=l&&tree[k].r<=r)
return tree[k].mx;
if(tree[k].lazy)
down(k);//询问到之前需要修改的区间
in mid=(tree[k].l+tree[k].r)/2;
in suml,sumr;
suml=sumr=0;
if(l<=mid)
suml=query(k*2,l,r);
if(r>=mid+1)
sumr=query((k*2)+1,l,r);
return suml+sumr;
}
int main()
{
in n,m;
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
build(1,1,n);
//for(int i=1;i<=4*n;i++)
//printf("p[%d]=%d\n",i,tree[i].mx);
char c;
in a,b,v;
while(m--)
{
getchar();
scanf("%c",&c);
if(c=='Q'){
scanf("%lld%lld",&a,&b);
printf("%lld\n",query(1,a,b));
}
else {
scanf("%lld%lld%lld",&a,&b,&v);
updata(1,a,b,v);
}
}
return 0;
}