最近突发奇想,到B站上看qscqesze神犇的每周算法讲堂,于是便学习了分块这个算法。
分块是一个很暴力的算法,按照某大神的说法,一般的区间问题都可以用他来解决,没有100分也有80分(一般会有80分,运气好有100分)。
分块是一个很暴力的算法,它可以完成几乎所有区间更新和区间查询问题,但效率相对于线段树等数据结构要差一些。
思想
对于一个长度为n的序列,我们可以讲其中的元素分为M个连续的子序列,每块的长度自然就为N/M。我们在更新一段区间[l,r]是,可以先更新l到l所在块的右端点和r所在块的右端点到r。即下图中红色的区域,每块中最多有N/M个元素,所以这一操作的复杂度的为N/M。
然后我们在成段更新刚才更新的块中间的那些块(即上图中红色区域中间的那些块),这些块最多为N块,所以这一操作的复杂度为M。
总操作的复杂度即为M+N/M,根据均值不等式可知,M=sqart(n)时复杂度最新。
建立分块序列
block代表每一块有多大,num代表一共有几块,belong【i】代表原序列中第i个元素在第几块,l[i]代表第i块的左端点,r[i]代表第i块的右端点。
具体怎么算我就不解释了,自己根据代码YY吧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
int build()
{
block = sqrt(n);
num = n/block;
if(n%block)//除不尽,多出一块
num++;
for(int i;i<=n;i++)
belong[i] = (i-1)/block+1,d[i] = a[i];
for(int i=1;i<=num;i++)
{
l[i] = (i-1)*block+1;
r[i] = i*block;
}
r[num] = n;//制定后一块的右端点为n
}
|
具体的查询和更新操作就不张贴代码了,因为这些因题目的要求而异。
最后附上 BZOJ 3343 /luogu 2801 教主的魔法的代码
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 1000002;
int n,q,a[1000002],num,block,belong[maxn],l[maxn],r[maxn],p[maxn],d[maxn];
int read()
{
int f=1,ans=0;char ch = getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch = getchar();}
while(ch>='0'&&ch<='9'){ans = ans*10 + ch - '0'; ch = getchar();}
return f*ans;
}
int build()
{
block = sqrt(n);
num = n/block;
if(n%block)
num++;
for(int i;i<=n;i++)
belong[i] = (i-1)/block+1,d[i] = a[i];
for(int i=1;i<=num;i++)
{
l[i] = (i-1)*block+1;
r[i] = i*block;
}
r[num] = n;
for(int i=1;i<=num;i++)
sort(d+l[i],d+r[i]+1);
}
int update(int L,int R,int W)
{
if(belong[L]==belong[R])
{
for(int i=L;i<=R;i++)
a[i]+=W;
for(int i=l[belong[L]];i<=r[belong[L]];i++)
d[i] = a[i];
sort(d+l[belong[L]],d+r[belong[L]]+1);
}
else
{
for(int i=L;i<=r[belong[L]];i++)
a[i]+=W;
for(int i=l[belong[L]];i<=r[belong[L]];i++)
d[i] = a[i];
sort(d+l[belong[L]],d+r[belong[L]]+1);
for(int i=l[belong[R]];i<=R;i++)
a[i]+=W;
for(int i=l[belong[R]];i<=r[belong[R]];i++)
d[i] = a[i];
sort(d+l[belong[R]],d+r[belong[R]]+1);
for(int i=belong[L]+1;i<=belong[R]-1;i++)
{
p[i]+=W;
}
}
}
int ask(int L,int R,int C)
{
int ans = 0;
if(belong[L]==belong[R])
{
for(int i=L;i<=R;i++)
if(a[i]+p[belong[i]]>=C)
ans++;
printf("%d\n",ans);
}
else
{
for(int i=L;i<=r[belong[L]];i++)
if(a[i]+p[belong[i]]>=C)
ans++;
for(int i=l[belong[R]];i<=R;i++)
if(a[i]+p[belong[i]]>=C)
ans++;
for(int i=belong[L]+1;i<belong[R];i++)
{
int ll = l[i],rr = r[i],result=0,mid;
while(ll<=rr)
{
mid = (ll+rr)>>1;
if(d[mid]+p[i]>=C)
rr = mid-1,result=r[i]-mid+1;
else
ll = mid+1;
}
ans += result;
}
printf("%d\n",ans);
}
}
int main()
{
int A,B,C;
char ch[5];
n=read();q=read();
for(int i=1;i<=n;i++)
a[i] = read();
build();
while(q--)
{
scanf("%s",&ch);
A=read();B=read();C=read();
if(ch[0]=='A')
ask(A,B,C);
else
update(A,B,C);
}
return 0;
}
|