最近突發奇想,到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;
}
|