线段树,树状数组基础
2018-01-17 20:46
393 查看
线段树和树状数组基本操作
两者对比
线段树操作
单点修改
区间修改
单点查询
区间查询
组合操作
树状数组操作
单点修改
区间查询
区间修改
区间修改单点查询
区间修改区间查询
单点修改,logn
区间修改,logn (加lazy数组)
单点查找,logn
区间查找,logn
树状数组的操作有:
单点修改,logn
区间修改,logn
单点查询,logn
区间查询,logn
代码比较多,一点不要手误…
//往上更新,然后查询的时候,往下加
[b]区间修改区间查询[/b]
比如要查询[l,r]的区间和
$sum[i]=a[1]+a[2]+…+a[i]+delta[1]i+delta[2](i-1)+…+delat[i]
=∑a[x]+(i+1)∗∑delta[x]+∑x∗delta[x]
sum[i]:表示从1到i的区间和,delta[i]:表示i到n的增量,那么就相当于两个树状数组操作
sum[l…r]=sum[r]-sum[l-1]
两者对比
线段树操作
单点修改
区间修改
单点查询
区间查询
组合操作
树状数组操作
单点修改
区间查询
区间修改
区间修改单点查询
区间修改区间查询
线段树和树状数组基本操作
线段树的操作有:单点修改,logn
区间修改,logn (加lazy数组)
单点查找,logn
区间查找,logn
树状数组的操作有:
单点修改,logn
区间修改,logn
单点查询,logn
区间查询,logn
两者对比
树状数组和线段树相比,空间减少大概4倍,时间较快,代码量少。但是线段树便于理解,树状数组很巧妙.线段树操作
单点修改
//将下标x的值修改为y void sert(int i,int l,int r,int x,int y) { if(l==r){ v[i]=y; return; } int mid = (l+r)>>1; if(x<=mid) sert(i<<1,l,mid,x,y); else sert(i<<1|1,mid+1,r,x,y); v[i]=v[i<<1]+v[i<<1|1]; }
区间修改
void upd(int i,int l,int r,int mid) { v[i<<1]+=(ll)(mid-l+1)*lazy[i]; v[i<<1|1]+=(ll)(r-mid)*lazy[i]; lazy[i<<1]+=lazy[i];lazy[i<<1|1]+=lazy[i]; lazy[i]=0; } //将区间[L,R]的值增加x void change(int i,int l,int r,int L,int R,int x) { if(l==L&&r==R){ v[i]+=(ll)x*(R-L+1); lazy[i]+=(ll)x; return; } int mid = (l+r)>>1; if(lazy[i]) upd(i,l,r,mid); if(R<=mid) change(i<<1,l,mid,L,R,x); else if(L>mid) change(i<<1|1,mid+1,r,L,R,x); else { change(i<<1,l,mid,L,mid,x); change(i<<1|1,mid+1,r,mid+1,R,x); } v[i]=v[i<<1]+v[i<<1|1]; }
单点查询
int query(int i,int l,int r,int x) { if(l==r) return v[i]; int mid = (l+r)>>1; if(x<=mid) return query(i<<1,l,mid,x); else return query(i<<1|1,mid+1,r,x); }
区间查询
void query(int i,int l,int r,int L,int R) { if(l==L&&r==R){ ans+=v[i]; return; } int mid = (l+r)>>1; if(lazy[i]) upd(i,l,r,mid);//如果没有区间修改的话,这个可以去掉 if(R<=mid) query(i<<1,l,mid,L,R); else if(L>mid) query(i<<1|1,mid+1,r,L,R); else { query(i<<1,l,mid,L,mid); query(i<<1|1,mid+1,r,mid+1,R); } }
组合操作
线段树的单点修改区间查询,区间修改单点查询,单点修改单点查询,区间修改区间查询就是上面单个操作的组合呀,就是区间修改的时候,可能有时候lazy会打的有点麻烦。代码比较多,一点不要手误…
树状数组操作
单点修改
这里给的是单点增加一个值,如果要改单点的值的话,那么就加上它们的差就行了//将id位置的值,增加x void change(int id,int x){ while(id<=n){ c[id]+=x; id = id+lowbit(id); } }
区间查询
如果要查询l..r区间的和,sum[r]-sum[l-1]就行了//查询区间1...x的和 int sum(int x){ int ans = 0; while(x){ ans += c[x]; x -= lowbit(x); } return ans; }
区间修改
[b]区间修改单点查询[/b]//往上更新,然后查询的时候,往下加
//一个数组a,开始时候有n个值,然后m个操作,1 x y z表示对将a[x]...a[y]的值都增加z //2 x 查询x点的值 int lowbit(int k){ return k&(-k); } void change(int k,int num) { if(k==0) return ; while(k<=n) { c[k]+=num; k+=lowbit(k); } return; } int Sum(int k) { int ans=0; while(k>0) { ans+=c[k]; k-=lowbit(k); } return ans; } int main() { cin>>n>>m; int t; for(int i=1;i<=n;i++) { cin>>a[i]; change(i,a[i]); change(i+1,-a[i]); } int t1,t2,t3,t4; for(int i=1;i<=m;i++) { cin>>t1; if(t1==1) {cin>>t2>>t3>>t4;Update(t2,t4);Update(t3+1,-t4);} if(t1==2) {cin>>t2; cout<<Sum(t2)<<endl;} } return 0; }
[b]区间修改区间查询[/b]
比如要查询[l,r]的区间和
$sum[i]=a[1]+a[2]+…+a[i]+delta[1]i+delta[2](i-1)+…+delat[i]
=∑a[x]+(i+1)∗∑delta[x]+∑x∗delta[x]
sum[i]:表示从1到i的区间和,delta[i]:表示i到n的增量,那么就相当于两个树状数组操作
sum[l…r]=sum[r]-sum[l-1]
using namespace std; #define ll long long const int maxn = 2e5+10; ll sum[maxn],delta[maxn],deltai[maxn]; int n,q,x,l,r; int lowbit(int x){ return x&(-x); } void change(ll *a,int i,int x){ while(i<=n){ a[i]+=x; i+=lowbit(i); } } ll query(ll *a,int i) { ll ans = 0; while(i>0){ ans+=a[i]; i-=lowbit(i); } return ans; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&x); sum[i]=sum[i-1]+x; } scanf("%d",&q); while(q--) { scanf("%d",&x); if(x==1){ scanf("%d %d %d",&l,&r,&x); change(delta,l,x); change(delta,r+1,-x); change(deltai,l,l*x); change(deltai,r+1,-x*(r+1)); } else { scanf("%d %d",&l,&r); ll sum1 = (ll)sum[r]+(ll)(r+1)*query(delta,r)-query(delta a112 i,r); ll sum2 = (ll)sum[l-1]+(ll)(l)*query(delta,l-1)-query(deltai,l-1); printf("%lld\n",sum1-sum2); } } return 0; }
相关文章推荐
- 线段树,树状数组基础
- 线段树,树状数组基础
- HDU 1166 敌兵布阵 (线段树基础&树状数组基础)
- 区间问题:两道线段树和树状数组的基础练习
- 线段树,树状数组基础
- 线段树,树状数组基础
- 线段树,树状数组基础
- hdu 1116 线段树或树状数组(基础题)
- 线段树,树状数组基础
- 【ny-oj】-108-士兵杀敌(一)(树状数组,线段树,基础)
- 线段树,树状数组基础
- 线段树,树状数组基础
- 【ny-oj】-116-士兵杀敌(二)(树状数组,线段树,基础)
- 线段树和树状数组各过一次POJ2182
- 仔细理解线段树和树状数组
- poj-3468 线段树和树状数组的区间更新及求和
- HDU 4417 Super Mario--离线树状数组、划分树、线段树
- HDU 1166 敌兵布阵(树状数组 or 线段树 单点修改 区间求和)
- POJ 3468(树状数组 && 线段树)
- hdu 1166 树状数组 线段树