【模板】分块、树状数组、线段树最终版(

都是支持区间操作的数据结构,略有不同,各有所长
以P3374为例比较。
其中树状数组空间最小MAXN,分块和线段树都要约4*MAXN

分块

把区间分成sqrt(n)个块来维护,抽象程度最低,打起来较方便。
用时: 2772ms / 内存: 11257KB……ギリギリ(

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=500005;
struct node{
int sum;//一个块的和
int l;//一个块的左端点(起始点)
int r;//一个块的右端点(终结点)
int bl;//第i个元素属于第blk[i].belong个块
};
int a[MAXN];
node blk[MAXN];//分块
int len,num;//len表示块的大小,num表示块的数量。

void build(int n){
int i,j;
len=sqrt(n);//块的大小是根号n
num=n/len;
if (n%len) num++;//有可能n不是完全平方数,这时块数需要加一
for(i=1;i<=num;i++){
blk[i].l=(i-1)*len+1;
blk[i].r=i*len;
}//对于左端点和右端点处理
for(i=1;i<=n;i++)
blk[i].bl=(i-1)/len+1;
blk[num].r=n;//最后一个块的右端点一定为n

for(i=1;i<=num;i++)
for(j=blk[i].l;j<=blk[i].r;j++)
blk[i].sum+=a[j];//预处理出所有块的和
}

inline void update(int x,int y){//第x个元素加上y
a[x]+=y;
blk[blk[x].bl].sum+=y;//第x个元素所在的块的总和也要加上y
}

inline int query(int x,int y){
int ans=0,i;
if (blk[x].bl==blk[y].bl){
for(i=x;i<=y;i++)
ans+=a[i];
return ans;
}
for(i=x;i<=blk[blk[x].bl].r;i++)
ans+=a[i];
for(i=blk[x].bl+1;i<=blk[y].bl-1;i++)
ans+=blk[i].sum;
for(i=blk[blk[y].bl].l;i<=y;i++)
ans+=a[i];
return ans;
}

int main(){
int n,m,x,y,t;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);

build(n);

for(int i=1;i<=m;i++){
scanf("%d%d%d",&t,&x,&y);
if(t==1){
update(x,y);
}
else{
printf("%d\n",query(x,y));
}
}
return 0;
}

有个奇怪的地方:
写成一个结构体数组AC了,分开写多个数组TLE了(可能也就快一点吧),要注意一下。


树状数组

用时: 672ms / 内存: 4019KB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include<cstdio>
#include<iostream>
using namespace std;
const int MAXN=500005;
int a,tree[MAXN]={0};
int n,m;
inline int lowbit(int x){
return x & (-x);
}

inline void add(int x,int y){
for(int i=x;i<=n;i+=lowbit(i))
tree[i]+=y;
}

inline int sum(int x){
int ans=0;
for(int i=x;i>0;i-=lowbit(i))
ans+=tree[i];
return ans;
}

int main(){
int x,y,t;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a);
add(i,a);
}

for(int i=1;i<=m;i++){
scanf("%d%d%d",&t,&x,&y);
if(t==1)
add(x,y);
else
printf("%d\n",sum(y)-sum(x-1));
}
return 0;
}

线段树

用时: 1516ms / 内存: 11660KB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=500005;
int a[MAXN];
struct node{
int val;
int lazy;
};
node sgt[MAXN*4];

void build(int rt,int st,int ed){
sgt[rt].lazy=0;
if(st==ed){
sgt[rt].val=a[st];
return;
}
int mid=(st+ed)>>1;
build(rt<<1,st,mid);
build(rt<<1|1,mid+1,ed);
sgt[rt].val=sgt[rt<<1].val+sgt[rt<<1|1].val;
}

inline void pd(int rt){
if(sgt[rt].lazy!=0){
sgt[rt<<1].lazy+=sgt[rt].lazy;
sgt[rt<<1|1].lazy+=sgt[rt].lazy;

sgt[rt<<1].val+=sgt[rt].lazy;
sgt[rt<<1|1].val+=sgt[rt].lazy;
sgt[rt].lazy=0;
}
}

int query(int rt,int nst,int ned,int qst,int qed){
if(qst>ned||qed<nst)
return 0;
if(qst<=nst&&qed>=ned)
return sgt[rt].val;
pd(rt);
int mid=(nst+ned)>>1;
return query(rt<<1,nst,mid,qst,qed)+query(rt<<1|1,mid+1,ned,qst,qed);
}

void update(int rt,int nst,int ned,int ust,int ued,int val){
if(ust>ned||ued<nst)
return;
if(ust<=nst&&ued>=ned){
sgt[rt].lazy+=val;
sgt[rt].val+=val;
return;
}
pd(rt);
int mid=(nst+ned)>>1;
update(rt<<1,nst,mid,ust,ued,val);
update(rt<<1|1,mid+1,ned,ust,ued,val);
sgt[rt].val=sgt[rt<<1].val+sgt[rt<<1|1].val;

}

int main(){
int n,m,x,y,t;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);

build(1,1,n);

for(int i=1;i<=m;i++){
scanf("%d%d%d",&t,&x,&y);
if(t==1){
update(1,1,n,x,x,y);
}
else{
printf("%d\n",query(1,1,n,x,y));
}
}
return 0;
}

加上lazytag真的可以快不少。