标签:实现 查询 tail ret stdin lowbit add span 位置
树状数组是一类存储后缀和,更新后缀和,通过
lowbit
来限定后缀和的长度,利用二进制使得查询、更新的时间复杂度都在\(O(logn)\)的数据结构,码量十分小,常数优秀
注意:以下下代码部分未经过压力测试,不保证完全正确
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+2020;
int c[N],a[N],n,q;
int lowbit(int x) {
return x&-x;
}
int sum(int r) {
int ret=0;
while(r>0) ret+=c[r],r-=lowbit(r);
return ret;
}
void add(int x,int val) {
while(x<=n) c[x]+=val,x+=lowbit(x);
}
signed main() {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
cin>>n>>q;
for(int i=1; i<=n; ++i)
scanf("%lld",&a[i]),add(i,a[i]);
for(int i=1; i<=q; ++i) {
int opt,val,x;
scanf("%lld %lld %lld",&opt,&x,&val);
if(opt==1) a[x]+=val,add(x,val);
else if(opt==2) cout<<sum(val)-sum(x-1)<<‘\n‘;
}
return 0;
}
利用差分数组还原原数组的方法即可实现单点查询
显然差分数组可以快速修改区间
/*
@ author:pyyyyyy/guhl37
-----思路------
-----debug-------
*/
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e6+2020;
int delta[N],n,q,a[N];
int lowbit(int x) {
return x&(-x);
}
void add(int x,int val) {
while(x<=n) delta[x]+=val,x+=lowbit(x);
}
int sum(int r) {
int ret=0;
while(r) ret+=delta[r],r-=lowbit(r);
return ret;
}
signed main() {
cin>>n>>q;
for(int i=1; i<=n; ++i)
scanf("%lld",&a[i]);
while(q--) {
int opt,l,r,x;
scanf("%lld",&opt);
if(opt==1) scanf("%lld %lld %lld",&l,&r,&x),add(l,x),add(r+1,-x);
else if(opt==2) scanf("%lld",&x),cout<<a[x]+sum(x)<<‘\n‘;
}
return 0;
}
对一个差分数组做一次前缀和可以得到每个位置的值再对每个位置累加一下就是一个区间的值
对于差分数组\(delta\)
差分数组的前缀和为\(val_i=\sum\limits_{j=1}^idelta_j\)
对于区间\([l,r]\)
\(s_{l,r}=\sum\limits_{i=1}^rval_i-\sum\limits_{i=1}^{l-1}val_i\)(前缀和相减的形式)
可以发现,一个区间的值实际上就是差分数组前缀和的前缀和做减法
我们可以用树状数组维护差分数组前缀和的前缀和
\(s_p=\sum\limits_{i=1}^p\sum\limits_{j=1}^idelta_j\)
\(s_p=\sum\limits_{i=1}^p\left(p-i+1\right)c_i=\left(p+1\right)\sum\limits_{i=1}^pc_i-\sum\limits_{i=1}^pi *c_i\)
显然这些东西都可用树状数组维护一下
/*
@ author:pyyyyyy/guhl37
-----思路------
-----debug-------
add里面为什么条件是<=N?
*/
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2000005;
int n,q;
int lowbit(int x) {
return x&-x;
}
void add(int *arr,int x,int val) {
while(x<=N) arr[x]+=val,x+=lowbit(x);
//这是为什么是x<=N?
}
int sum(int *arr,int x) {
int ret=0;
while(x) ret+=arr[x],x-=lowbit(x);
return ret;
}
int a[N],d[N],id[N];
int ans(int k) {
return k*sum(d,k)-sum(id,k);
}
signed main() {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
cin>>n>>q;
for(int i=1; i<=n; ++i) {
scanf("%lld",&a[i]);
add(d,i,a[i]-a[i-1]);
add(id,i,(i-1)*(a[i]-a[i-1]));
}
while(q--) {
int opt,l,r,x;
cin>>opt;
if(opt==1) {
scanf("%lld %lld %lld",&l,&r,&x);
add(d,l,x),add(d,r+1,-x);
add(id,l,(l-1)*x),add(id,r+1,-r*x);
} else if(opt==2) {
scanf("%lld %lld",&l,&r);
cout<<ans(r)-ans(l-1)<<‘\n‘;
}
}
return 0;
}
上面有个不太懂的地方,恳请大佬解答
没想到树状数组能干这个 ,其实常数也蛮大的了,没什么意义,还不如写线段树
void build(int n){
for(int i=1;i<=n;++i)
{
c[i]=a[i];int t=lowbit(i);
for(int j=1;j<t;j*=2) c[i]=max(c[i],c[i-j]);
}
}
void add(int pos,int x)
{
a[pos]=x;
while(pos<=n){
c[pos]=a[pos];int t=lowbit(i);
for(int j=1;j<t;j*=2) c[i]=max(c[i],c[i-j]);
pos+=lowbit(pos);
}
}
int query(int l,int r)
{
int ans=a[r];
while(1)
{
ans=max(ans,num[r]);
if(r==1) break;
r--;
while(r-l>=lowbit(r)) ans=max(ans,c[r]),r-=lowbit(r);
}
return ans;
}
标签:实现 查询 tail ret stdin lowbit add span 位置
原文地址:https://www.cnblogs.com/pyyyyyy/p/13190339.html