树状数组 学习笔记
定义
树状数组(Binary Indexed Tree,BIT)是一种数据结构,可以高效地维护一些单点修改的问题。搬一张 OI-wiki 的图:
设原数组为 \(a\),树状数组为 \(c\),有:
引入一种新的运算 \(\operatorname{lowbit}\) 表示取一个数二进制最低位的 \(1\),如 \(\operatorname{lowbit}((10100)_2)=(00100)_2,\operatorname{lowbit}((10001)_2)=(00001)_2\),则树状数组中,\(c_i\) 表示区间 \([i-\operatorname{lowbit}(i)+1,i]\)。
\(\operatorname{lowbit}\) 有一个经典的求法是 x&-x。这是由于 \(-x\) 的补码是 \(x\) 取反再加一,而一个数加一相当于取反最低位 \(1\) 与更低位的 \(0\),那么 x&-x 只保留最低位的一。
普通树状数组
单点修改,区间查询
对一个点修改,显然只影响其祖先。因此可以由叶节点不断向上跳,每次加上 \(\operatorname{lowbit}\)。
for(;x<=n;x+=(x&-x))tr[x]+=k;
区间查询可以差分成两段前缀和。利用树状数组的结构,如果区间 \([1,r]\),可以每次对 \(r\) 减 \(\operatorname{lowbit}\) 并加上树状数组上对应的节点。
for(;x;x-=(x&-x))ans+=tr[x];
另外如果修改变成减 \(\operatorname{lowbit}\),查询变成加 \(\operatorname{lowbit}\),就成了后缀树状数组,查询的是后缀和。这相当于把 \(c_i\) 代表的区间变成 \([i,i+\operatorname{lowbit}-1]\)。
建树
最简单的想法是执行 \(n\) 次单点修改,时间复杂度 \(O(n\log n)\)。
有两种方法可以 \(O(n)\) 建树:
- \(c_i\) 表示区间 \([i-\operatorname{lowbit}(i)+1,i]\),可以前缀和直接算。
for(int i=1;i<=n;i++)a[i]+=a[i-1],tr[i]=a[i]-a[i-(i&-i)];
- 每一个结点会贡献给父节点,可以计算贡献。
for(int i=1;i<=n;i++){
tr[i]+=a[i];
if(i+(i&-i)<=n)tr[i+(i&-i)]+=tr[i];
}
区间修改,单点查询
对原数组进行差分。那么修改 \([l,r]\) 变为 \(a_l\gets a_l+k,a_{r+1}\gets a_{r+1}-k\),查询 \(x\) 处的值变为查询 \(\sum_{i=1}^n a_i\)。这是一个单点修改,区间查询问题。
区间修改,区间查询
记原数组 \(a\) 的差分数组为 \(b\),则 \(a_i=\sum_{j=1}^ib_j\)。
同时维护 \(b_i,ib_i\) 即可。
二维树状数组
单点修改,区间查询
二维树状数组是一种树套树,外层树状数组的每一个结点都是一个树状数组,维护的是二维前缀和。
单点加时双重循环,先遍历外层的树状数组,对于每个外层的节点再遍历内层。
for(int i=x;i<=n;i+=(i&-i))for(int j=y;j<=m;j+=(j&-j))tr[i][j]+=k;
区间查询同理。
for(int i=x;i;i-=(i&-i))for(int j=y;j;j-=(j&-j))ans+=tr[i][j];
将询问差分,答案为 \(sum_{c,d}-sum_{a-1,d}-sum_{c,b-1}+sum_{a-1,b-1}\)。
区间修改,单点查询
类似一维树状数组,使用差分把问题转化为单点修改,区间查询。修改变为 add(c+1,d+1,k),add(a,d+1,-k),add(c+1,b,-k),add(a,b,k),查询时直接取该位置的前缀和。
区间修改,区间查询
维护原数组 \(a\) 的差分数组 \(b\),\(b\) 作两次前缀和就变成了 \(a\) 的前缀和。
维护 \(b_{i,j},(i-1)b_{i,j},(j-1)b_{i,j},(i-1)(j-1)b_{i,j}\)。
树状数组上倍增
树状数组也可以二分。初始时位置 \(x=0\),从大到小枚举 \(2^i\),如果加上区间 \([x+1,x+2^i]\) 还是满足条件,就将 \(x\) 往后跳 \(2^i\) 并合并这个区间的信息,最终的 \(x\) 就是最后一个满足条件的位置。而区间 \([x+1,x+2^i]\) 正是 \(x+2^i\) 的信息,因为 \(x\) 加上的位都比 \(2^i\) 大,\(\operatorname{x+2^i}=2^i\)。
如果从某个位置开始二分也类似,让 \(x\) 小于左端点时一直加即可。
将温度离散化。如果温度为下标,可用的冰系战士是一段前缀,火系是一段后缀,能量和都是单调的。而双方消耗的总能量是两边能量的最小值乘二,这是一个单峰函数。
那么可能的答案在峰顶两侧。左侧的答案就是最后一个冰系能量小于火系能量的位置。对于右侧,先找到第一个冰系能量大于等于火系能量的位置(即左侧的答案加一)。由于可能有若干位置火系能量不变,依题意找出最晚的火系能量相同的位置。
用树状数组维护冰系火系的能量,可以树状数组上倍增。
#include<bits/stdc++.h>
using namespace std;
char buf1[2097152],*ip1=buf1,*ip2=buf1;
inline int getc(){
return ip1==ip2&&(ip2=(ip1=buf1)+fread(buf1,1,2097152,stdin),ip1==ip2)?EOF:*ip1++;
}
template<typename T>void in(T &a)
{
T ans=0;
char c=getc();
for(;c<'0'||c>'9';c=getc());
for(;c>='0'&&c<='9';c=getc())ans=ans*10+c-'0';
a=ans;
}
template<typename T,typename... Args>void in(T &a,Args&...args)
{
in(a),in(args...);
}
int n,temp[2000005],cnt;
long long tr0[2000005],tr1[2000005],sum;
struct query{
int opt,t,x,k;
long long y;
}q[2000005];
void add(long long tr[],int x,long long k){
for(;x<=n;x+=(x&-x))tr[x]+=k;
}
long long query(long long tr[],int x,long long ans=0){
for(;x;x-=(x&-x))ans+=tr[x];
return ans;
}
int main(){
in(n);
for(int i=1;i<=n;i++){
in(q[i].opt);
if(q[i].opt==1)in(q[i].t,q[i].x,q[i].y),temp[++cnt]=q[i].x;
else in(q[i].k);
}
sort(temp+1,temp+cnt+1),cnt=unique(temp+1,temp+cnt+1)-temp-1;
for(int i=1;i<=n;i++)if(q[i].opt==1)q[i].x=lower_bound(temp+1,temp+cnt+1,q[i].x)-temp;
for(int i=1;i<=n;i++){
if(q[i].opt==1){
if(q[i].t)add(tr1,q[i].x+1,q[i].y),sum+=q[i].y;
else add(tr0,q[i].x,q[i].y);
}
else{
if(q[q[i].k].t)add(tr1,q[q[i].k].x+1,-q[q[i].k].y),sum-=q[q[i].k].y;
else add(tr0,q[q[i].k].x,-q[q[i].k].y);
}
int p1=0,p2=0;
long long sum0=0,sum1=sum,ans1=0,ans2=0;
for(int i=20;i>=0;i--){
p1+=1<<i;
if(p1<=cnt&&sum0+tr0[p1]<sum1-tr1[p1])sum0+=tr0[p1],sum1-=tr1[p1];
else p1-=1<<i;
}
ans1=sum0,sum0=0,sum1=sum;
if(p1<cnt){
ans2=min(query(tr0,p1+1),sum-query(tr1,p1+1));
for(int i=20;i>=0;i--){
p2+=1<<i;
if(p2<=cnt&&(p2<=p1||min(sum0+tr0[p2],sum1-tr1[p2])==ans2))sum0+=tr0[p2],sum1-=tr1[p2];
else p2-=1<<i;
}
}
if(!ans1&&!ans2)puts("Peace");
else if(ans1>ans2)cout<<temp[p1]<<' '<<2*ans1<<'\n';
else cout<<temp[p2]<<' '<<2*ans2<<'\n';
}
return 0;
}
[[数据结构]]

浙公网安备 33010602011771号