树状数组基本模板
三个基本函数(核心代码)
int lowbit(int x)
{
return x & -x;
}
void add(int x,int k)//在第x个位置上加上k
{
while(x<=n)
{
tree[x]+=k;
x+=lowbit(x);
}
}
int getsum(int x)
{
int sum=0;
while(x)
{
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
一、单点修改,区间查询
1.输入
for(int i=1;i<=n;i++)
{
cin>>a[i];
add(i,a[i]);
}
2.单点修改
if(num==1)
{
add(l,r);
}
3.求区间【l,r】的和
else if(num==2)
{
cout<<getsum(r)-getsum(l-1)<<endl;
}
4.例题加代码
简要概括题目要求:1.输入数字1,给定i,x;将a[i]+x;输入数字2,给定l,r,求该区间的和
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+100;
int a[N],tree[N];
int n,q,l,r,num;
int lowbit(int x)
{
return x & -x;
}
void add(int x,int k)//单点修改
{
while(x<=n)
{
tree[x]+=k;
x+=lowbit(x);
}
}
int getsum(int x)
{
int sum=0;
while(x)
{
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
signed main()
{
cin>>n>>q;
int i;
for(i=1;i<=n;i++)
{
cin>>a[i];
add(i,a[i]);//!!!
}
while(q--)
{
cin>>num>>l>>r;
if(num==1)
{
add(l,r);
}
else
{
cout<<getsum(r)-getsum(l-1)<<endl;
}
}
return 0;
}
二、区间修改,单点查询
1.输入
for(int i=1;i<=n;i++)
{
cin>>a[i];
add(i,a[i]-a[i-1]);//差分数组
}
2.区间修改
由于树状数组只支持单点修改,所以可以利用差分数组,将区间操作,转变为单点操作
if(num==1)
{
cin>>l>>r>>k;//将区间操作改为单点操作
add(l,k);
add(r+1,-k);
}
3.单点查询
else
{
cin>>y;
cout<<getsum(y)<<endl;
}
4.例题+代码
题目概述:1 l r k,在区间【l,r】上,给a[i]+k;2 i ,求a[i]的值
#include<iostream>
#include<string.h>
#include<algorithm>
#define int long long
using namespace std;
const int N=1e6+100;
int a[N],tree[N];//树状数组只支持单点修改,区间操作转变为单点操作
int n,q,l,r,k,num,y;
int lowbit(int x)
{
return x & -x;
}
void add(int x,int k)
{
while(x<=n)
{
tree[x]+=k;
x+=lowbit(x);
}
}
int getsum(int x)
{
int sum=0;
while(x)
{
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
signed main()
{
cin>>n>>q;
int i;
for(i=1;i<=n;i++)
{
cin>>a[i];
add(i,a[i]-a[i-1]);//差分数组
}
while(q--)
{
cin>>num;
if(num==1)
{
cin>>l>>r>>k;//将区间操作改为单点操作
add(l,k);
add(r+1,-k);
}
else
{
cin>>y;
cout<<getsum(y)<<endl;
}
}
return 0;
}
三、区间修改,区间查询
1.输入
for(int i=1;i<=n;i++)
{
cin>>a[i];
b[i]=a[i]-a[i-1];//差分数组
add(i,b[i]);
add2(i,(i-1)*b[i]);//对于a的差分数组,再建立一个新的树状数组
}
原理:
tree[i]=a[i]-a[i-1]//差分
a[i]=tree[1]+tree[2]+...+tree[i];
a[1]+a[2]+...+a[r]
=tree[1]+(tree[1]+tree[2])+...+(tree[1]+tree[2]+...+tree[r]);
=r*(tree[1])+(r-1)*tree[2]+...+tree[r];
结论:copyA[i]=tree[i]*(i-1)//对于a的差分数组,再建立一个新的树状数组
2.区间修改
if(num==1)
{
cin>>l>>r>>x;
add(l,x);
add(r+1,-x);
add2(l,(l-1)*x);
add2(r+1,(r+1-1)*-x);
}
3.区间查询
else
{
cin>>l>>r;
int ans1=r*getsum(r)-getsum2(r);
int ans2=(l-1)*getsum(l-1)-getsum2(l-1);
cout<<ans1-ans2<<endl;
}
4.例题+代码
题目概述:1 l r x,在区间【l,r】上,使得该区间里的a[i]+x;
2 l r,求区间【l,r】里,所有a[i]的和
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
int a[N],b[N];
int tree[N],copyA[N];
int n,q;
int l,r,x,num;
int lowbit(int x)
{
return x & -x;
}
void add(int x,int k)
{
while(x<=n)
{
tree[x]+=k;
x+=lowbit(x);
}
}
void add2(int x,int k)
{
while(x<=n)
{
copyA[x]+=k;
x+=lowbit(x);
}
}
int getsum(int x)
{
int sum=0;
while(x)
{
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
int getsum2(int x)
{
int sum=0;
while(x)
{
sum+=copyA[x];
x-=lowbit(x);
}
return sum;
}
signed main()
{
cin>>n>>q;
int i;
for(i=1;i<=n;i++)
{
cin>>a[i];
b[i]=a[i]-a[i-1];
add(i,b[i]);
add2(i,(i-1)*b[i]);
}
while(q--)
{
cin>>num;
if(num==1)
{
cin>>l>>r>>x;
add(l,x);
add(r+1,-x);
add2(l,(l-1)*x);
add2(r+1,(r+1-1)*-x);
}
else
{
cin>>l>>r;
int ans1=r*getsum(r)-getsum2(r);
int ans2=(l-1)*getsum(l-1)-getsum2(l-1);
cout<<ans1-ans2<<endl;
}
}
return 0;
}