树状数组基础
解决问题
1.树状数组可以解决“动态修改动态查询区间和”的问题(前缀和是多次修改完多次查询),不过一般解决单点修改+区间查询问题
2.树状数组先要了解lowbit操作,这是一种二进制操作,取一个数字二进制表达中最低位的1以及后面所有的0,例如:
lowbit(4)=lowbit(100)=100=4
lowbit(6)=lowbit(110)=10=2
lowbit函数代码:
int lowbit(int x){
return x & -x;
}
3.树状数组即一个数组int t[N]
,表示为 t [ i ] = ∑ j = i − l o w b i t ( i ) + 1 i a [ i ] t[i]=\sum_{j=i-lowbit(i)+1}^{i} a[i] t[i]=j=i−lowbit(i)+1∑ia[i]
即维护一个长为lowbit(x)的[x-lowbit[x]+1,x]
的区间和
4.单点修改函数,如下图,修改a[3]+k
,就是t[3]+k
,t[4]+k
,t[8]+k
,而3+lowbit(3)=4
,4+lowbit(4)=8
,直到小于等于n
//给a[k]增加x
void update(int k,int x){
for(int i=k;i<=n;i+=lowbit(i)){
t[i]+=x;
}
}
5.区间查询函数:如下图,查询区间[3,7]
,即获得[1,7]-[1,2]
,而[1,7]=t[7]+t[6]+t[4]
,而7-lowbit(7)=6,6-lowbit(6)=4
,直到为0(不能等于0,不然死循环,所以树状数组开始不能为0)
//获取[1,k]的区间和
void getprefix(int k){
int res=0;
for(int i=k;i>0;i-=lowbit(i)){
res+=t[i];
}
return res;
}
![[树状数组.png]]
殷老师排队
学习:
(1)记得输入a[i]
的时候**update(i,a[i])
初始化t数组**
(2)单点修改不仅要调用update函数,还有记得修改a[i]
数组
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],t[N],n,m;
int lowbit(int x){
return x & (-x);
}
//第x个学生的魅力值增加k
void update(int x,int k){
for(int i=x;i<=n;i+=lowbit(i)){
t[i]+=k;
}
}
//获取[1,x]的学生的魅力值和
int getprefix(int x){
int res=0;
for(int i=x;i>0;i-=lowbit(i)){
res+=t[i];
}
return res;
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
update(i,a[i]); //初始化t数组
}
for(int i=1;i<=m;i++){
int op;
cin>>op;
if(op==1){
int x,z;
cin>>x>>z;
update(x,z-a[x]);
a[x]=z; //记得更新a[x]
}
else{
int x;
cin>>x;
cout<<(2*x-n)*a[x]+getprefix(n)-2*getprefix(x)<<endl;
}
}
return 0;
}
拼十字
学习:
(1)此题暴力筛选会超时,充要条件是矩形 1 的长度严格大于矩形 2 的长度并且矩形 1 的宽度严格小于矩形 2 的宽度,就可以给结构体矩形首先按长度降序排列(如果长度一样则按宽度降序排列,保证不会选这两个),然后问题就转化为求宽度小于当前矩形的宽度的矩形数量和,就可以以宽度为索引id,a[i]
表示宽度为i的矩形数量,输入一个矩形就是单点增加a[i]+1
,查询就是[1,宽度i-1]
的数量前缀和,单点修改+区间查询可以用动态数组解决
(2)而三种颜色就是三个树状数组,用类来解决
(3)类构造初始化列表:
Tree(ll size):t(size+1,0){}
等价于:
Tree(ll size){t.resize(size+1,0);}
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10,mod=1e9+7;
int n;
ll ans;
class Tree{
private:
vector<ll> t;
public:
Tree(ll size):t(size+1,0){} // 使用初始化列表
//Tree(ll size){t.resize(size+1,0);}// 使用 resize 初始化
int lowbit(int x){
return x & (-x);
}
//宽度x的数量++
void update(int x){
for(int i=x;i<=N;i+=lowbit(i)){ //宽度最大到N,不是n
t[i]++;
}
}
//返回[1,x]的和
ll get(int x){
ll res=0;
for(int i=x;i>0;i-=lowbit(i)){
res+=t[i];
}
return res;
}
};
struct Rec{
int l,w,c;
}r[N];
bool cmp(Rec &r1,Rec &r2){
if(r1.l!=r2.l) return r1.l>r2.l; //降序排列,树状数组即求小于当前宽度的矩形个数即可
//相等则按宽度降序排列,保证不会考虑到
return r1.w>r2.w;
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>r[i].l>>r[i].w>>r[i].c;
}
sort(r+1,r+n+1,cmp);
//初始化三个颜色的树状数组
Tree T_R(N); //宽度为索引
Tree T_Y(N);
Tree T_B(N);
for(int i=1;i<=n;i++){
if(r[i].c==0){
ans= (ans+T_Y.get(r[i].w-1)+T_B.get(r[i].w-1))%mod;
T_R.update(r[i].w);
}
else if(r[i].c==1){
ans= (ans+T_R.get(r[i].w-1)+T_B.get(r[i].w-1))%mod;
T_Y.update(r[i].w);
}
else{
ans= (ans+T_R.get(r[i].w-1)+T_Y.get(r[i].w-1))%mod;
T_B.update(r[i].w);
}
}
cout<<ans;
return 0;
}