本来想练练线段树的,没想到有许多细节忘了,加上今天的金工实习坐牢坐穿了,于是再复习一下吧。
首先介绍一下树状数组(貌似第一篇就讲了,不过那个东西真是一坨Shit,当时还没有怎么理解就写了)
首先它的复杂度为logn,主要功能是动态的给某个位置加上一个数以及求某一个前缀和。
我们发现它包含于线段树,不过他的常数小并且实现简单。
我们先不讲原理,下面直接看它的实现方式(注意:下标从1开始):
其中第0层即它没有2的因子(奇数)。
第二层即有2^1,但没有2^2的数,依次类推。。。。。
我们假设x二进制最后有k个0,那么它表示从x-2^k---x的区间和。
即c[x]=(x-lowbit(x),x](c[x]表示其代表的值)。
这样子,我们如何求某一个前缀和:c[x]+c[x-lowbit[x]]+.....即可。
那么怎么更新?假如更新7,那么8,16的值也需要更新。可得我们从它开始依次加lowbit即可(证明省略)
来个模板题:
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,m,a[N],tr[N];
int lowbit(int x){return x&(-x);
}
void add(int x,int v){for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=v;
}
int query(int x){int res=0;for(int i=x;i>0;i-=lowbit(i)){res+=tr[i];}return res;
}
int main(){cin>>n>>m;for(int i=1;i<=n;i++) scanf("%d",&a[i]);for(int i=1;i<=n;i++) add(i,a[i]);while(m--){int k,x,y;scanf("%d%d%d",&k,&x,&y);if(k==0) printf("%d\n",query(y)-query(x-1));else add(x,y);}
}
来个简单的应用(二维降一维):
其实我们发现,在时间的顺序下问题就是求1---xi的前缀和(没有给t的话就先排一下序,这样子就十分巧妙地转化成了一维,这样就转化成了刚才的模板题。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N=32010;
int n,x,y,tr[N],level[N];
int lowbit(int x){return x&(-x);
}
int add(int x){for(int i=x;i<=N;i+=lowbit(i)) tr[i]++;
}
int sum(int x){int res=0;for(int i=x;i;i-=lowbit(i)) res+=tr[i];return res;
}
int main(){cin>>n;for(int i=0;i<n;i++){scanf("%d%d",&x,&y);x++;level[sum(x)]++;add(x);}for(int i=0;i<n;i++) printf("%d\n",level[i]);
}
接下来个线段树的一个模板题练练手:
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,m,w[N];
struct node{int l,r;int maxv;
}tr[N*4];
void build(int u,int l,int r){if(l==r) tr[u]={l,r,w[l]};else{tr[u]={l,r};int mid=(l+r)/2;build(u<<1,l,mid);build(u<<1|1,mid+1,r);tr[u].maxv=max(tr[u<<1].maxv,tr[u<<1|1].maxv);}
}
int query(int u,int l,int r){if(tr[u].l>=l&&tr[u].r<=r) return tr[u].maxv;int mid=tr[u].l+tr[u].r>>1;int maxv=INT_MIN;if(l<=mid) maxv=query(u<<1,l,r);if(r>mid) maxv=max(maxv,query(u<<1|1,l,r));return maxv;
}
int main(){cin>>n>>m;for(int i=1;i<=n;i++) scanf("%d",&w[i]);build(1,1,n);int l,r;while(m--){scanf("%d%d",&l,&r);printf("%d\n",query(1,l,r));}
}
(这个静态的题用线段树纯属没事找事)