给定 n n 个数,有三种操作:给某个数加一个值,给某个数减一个值,询问一段区间数的总和(万能模板题)。
1≤n≤50000 1 ≤ n ≤ 50000
练习数据结构的模板题,有三种比较基本的数据结构可以解决,分块、树状数组、线段树,这里逐一介绍。
在我的理解中,分块并不是一种数据结构,而是一种思想。对一段连续的区间赋值,就当是 n n 吧,复杂度是 O(n) O ( n ) ,但如果将这个 n n 分成若干块,每块大小是 S S (可以不完整),平均下来有是 nS n S 块。每次修改碰到完整区间可以先打上标记,等下次需要查询这个区间部分的时候再解开标记向内部更新(用这次的复杂度完成上次的事情,延迟更新的思想)。这样一次修改、查询就都变成了 O(S+nS) O ( S + n S ) ,而基本不等式告诉我们,当 S S 取 n−−√ n 时,能保证复杂度尽可能小,是 O(n−−√) O ( n ) 级别的。
树状数组,我一般将它看作一种特殊的前缀数组,通过递归(写成循环)得到结果。设 a a 的树状数组为 c c ,那么 ci c i 有 lowbit(i) l o w b i t ( i ) 个元素,如 c6 c 6 有两个元素, c7 c 7 有一个元素。如在求 a1+a2+...+a6 a 1 + a 2 + . . . + a 6 时,先访问 c6 c 6 ,这样就得到了后 lowbit(6) l o w b i t ( 6 ) 即后两个元素的总和,然后原式就转化成了求 a1+a2+...+a4 a 1 + a 2 + . . . + a 4 的和。也就是求 a1+a2+...+an a 1 + a 2 + . . . + a n 实际上是将 n n 二进制位上的 1 1 从低到高逐步剥去。而给某个元素加上某个值,只是上述的逆运算罢了,不断加目前加到数 x x 的 lowbit(x) l o w b i t ( x ) 。这样也不难看出,复杂度都是 O(logn) O ( l o g n ) 级别的,而且常数非常小,一般不到 O(logn) O ( l o g n ) 次就完成了。
线段树,节点表示的是一段线段(区间)的树,无论是区间(单点)修改、区间(单点)查询,复杂度都能保证在 O(logn) O ( l o g n ) 级别,但常数比树状数组大。线段树我认为还是分治的思想体现的更到位。在要对区间 [L,R] [ L , R ] 查询或修改时,如果目前递归到的节点是 [l,r] [ l , r ] 我们只用取 [l,r] [ l , r ] 的中点 mid m i d ,如果 [L,R] [ L , R ] 完全在 mid m i d 的一边,那就只往那一边递归,否则向两边递归。线段树的区间修改也有分块的延迟更新思想在,比如当完整覆盖某个区间时,可以先对这个区间打上标记,等下次需要部分查询时再解开标记。
#include
#include
#include
#include
#include
#include
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define N 50003
typedef long long LL;
using namespace std;
template<const int maxn>struct Block
{
int a[maxn],sum[maxn],tag[maxn];
int bs,cnt;
Block(){bs=sqrt(maxn),cnt=(maxn+bs-1)/bs;}
void build(int *arr,int n)
{
memset(sum,0,sizeof(sum));
memset(tag,0,sizeof(tag));
FOR(i,1,n)
{
a[i]=arr[i];
sum[i/bs]+=a[i];
}
}
void push_down(int _)
{
if(!tag)return;
FOR(i,_*bs,(_+1)*bs-1)
a[i]+=tag[_];
tag[_]=0;
}
void update(int x,int y,int addval)
{
int kx=x/bs,ky=y/bs;
push_down(kx),push_down(ky);
if(kx==ky)
{
FOR(i,x,y)
{
a[i]+=addval;
sum[kx]+=addval;
}
return;
}
for(int i=x;i<(kx+1)*bs;i++)
{
a[i]+=addval;
sum[kx]+=addval;
}
for(int i=ky*bs;i<=y;i++)
{
a[i]+=addval;
sum[ky]+=addval;
}
FOR(i,kx+1,ky-1)
{
tag[i]+=addval;
sum[i]+=bs*addval;
}
}
int query(int x,int y)
{
int res=0,kx=x/bs,ky=y/bs;
push_down(kx),push_down(ky);
if(kx==ky)
{
FOR(i,x,y)res+=a[i];
return res;
}
for(int i=x;i<(kx+1)*bs;i++)
res+=a[i];
for(int i=ky*bs;i<=y;i++)
res+=a[i];
FOR(i,kx+1,ky-1)
res+=sum[i];
return res;
}
};
BlockB;
int a[N];
int main()
{
int T,n;
scanf("%d",&T);
FOR(Ti,1,T)
{
scanf("%d",&n);
FOR(i,1,n)scanf("%d",&a[i]);
printf("Case %d:\n",Ti);
B.build(a,n);
char str[10];
while(scanf("%s",str))
{
int x,y;
if(str[0]=='A'||str[0]=='S')
{
scanf("%d%d",&x,&y);
B.update(x,x,str[0]=='A'?y:-y);
}
else if(str[0]=='Q')
{
scanf("%d%d",&x,&y);
printf("%d\n",B.query(x,y));
}
else if(str[0]=='E')break;
}
}
return 0;
}
#include
#include
#include
#include
#include
#include
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define lowbit(x) ((x)&-(x))
#define N 50003
typedef long long LL;
using namespace std;
int sum[N],n;
void update(int x,int y)
{
while(x<=n)
{
sum[x]+=y;
x+=lowbit(x);
}
return;
}
int query(int x)
{
int res=0;
while(x)
{
res+=sum[x];
x^=lowbit(x);
}
return res;
}
int main()
{
int T,a;
scanf("%d",&T);
FOR(Ti,1,T)
{
memset(sum,0,sizeof(sum));
scanf("%d",&n);
printf("Case %d:\n",Ti);
FOR(i,1,n)
{
scanf("%d",&a);
update(i,a);
}
int x,y;
char str[10];
while(~scanf("%s",str))
{
if(str[0]=='Q')
{
scanf("%d%d",&x,&y);
printf("%d\n",query(y)-query(x-1));
}
else if(str[0]=='A'||str[0]=='S')
{
scanf("%d%d",&x,&y);
update(x,str[0]=='A'?y:-y);
}
else if(str[0]=='E')break;
}
}
return 0;
}
#include
#include
#include
#include
#include
#include
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define N 50003
typedef long long LL;
using namespace std;
struct node
{
int L,R,sum;
int lth(){return R-L+1;}
};
struct SegmentTree
{
node nd[N<<2];
void build(int k,int L,int R,int *arr)
{
nd[k].L=L,nd[k].R=R;
if(L==R)
{
nd[k].sum=arr[L];
return;
}
build(k<<1,L,L+R>>1,arr);
build(k<<1|1,(L+R>>1)+1,R,arr);
nd[k].sum=nd[k<<1].sum+nd[k<<1|1].sum;
}
void update(int k,int x,int addval)
{
if(nd[k].L==nd[k].R)
{
nd[k].sum+=addval;
return;
}
int mid=nd[k].L+nd[k].R>>1;
if(x<=mid)update(k<<1,x,addval);
if(mid1|1,x,addval);
nd[k].sum=nd[k<<1].sum+nd[k<<1|1].sum;
return;
}
int query(int k,int L,int R)
{
if(L<=nd[k].L&&nd[k].R<=R)return nd[k].sum;
int res=0,mid=nd[k].L+nd[k].R>>1;
if(L<=mid)res+=query(k<<1,L,R);
if(mid1|1,L,R);
return res;
}
}ST;
int a[N];
int main()
{
int T,n;
scanf("%d",&T);
FOR(Ti,1,T)
{
scanf("%d",&n);
FOR(i,1,n)scanf("%d",&a[i]);
printf("Case %d:\n",Ti);
ST.build(1,1,n,a);
char str[10];
while(~scanf("%s",str))
{
if(str[0]=='E')break;
int x,y;
scanf("%d%d",&x,&y);
if(str[0]=='Q')printf("%d\n",ST.query(1,x,y));
else if(str[0]=='A')ST.update(1,x,y);
else if(str[0]=='S')ST.update(1,x,-y);
}
}
return 0;
}