《算法竞赛进阶指南》读书笔记汇总
这里面是我在阅读《算法竞赛进阶指南》这本书时的一些思考,有兴趣可以瞧瞧!
如若发现什么问题,可以通过评论或者私信作者提出。希望各位大佬不吝赐教!
倍增基本思想
倍增,故名思意,就是成倍的增长。通常在递推时,由于状态空间过大使得线性时间复杂度无法满足要求,这是我们可以通过成倍增长的方式,只递推状态空间中在2的整数次幂位置上的值作为代表。当需要其他位置的值时,我们可以通过把该位置表示成若干个2的整数倍的和。这就要求问题的状态空间关于2的次幂具有可划分性。
在这里,我们只研究序列上的倍增问题。其他问题例如LCA等,将在0x63节探讨。
【例题】天才ACM(AcWing109)
题目链接
思路:
首先有个一个结论,对于一个集合
S
S
S,校验值就是取
S
S
S中最大
M
M
M个数与最小的
M
M
M个数,最大与最小构成一对,次大与次小构成一对,…计算所得的值。
我们来证明这个结论,假设
M
=
2
,
S
M=2,S
M=2,S中的最大值与最小值分别为
a
a
a与
b
b
b,次大值与次小值分别为
c
c
c与
d
d
d。那么如果最大与最小值为一对,次大与次小值为一对,校验值为
S
1
S_{1}
S1:
S
1
=
(
a
−
b
)
2
+
(
c
−
d
)
2
=
a
2
+
b
2
−
2
a
b
+
c
2
+
d
2
−
2
c
d
S_{1}=(a - b)^{2}+(c - d)^{2} =a^{2}+b^{2}-2ab+c^{2}+d^{2}-2cd
S1=(a−b)2+(c−d)2=a2+b2−2ab+c2+d2−2cd
如果最大和次小为一对,次大与最小为一对,那么校验值为
S
2
S_{2}
S2:
S
2
=
(
a
−
d
)
2
+
(
c
−
b
)
2
=
a
2
+
d
2
−
2
a
d
+
c
2
+
b
2
−
2
b
c
S_{2}=(a-d)^{2}+(c-b)^{2}=a^{2}+d^{2}-2ad+c^{2}+b^{2}-2bc
S2=(a−d)2+(c−b)2=a2+d2−2ad+c2+b2−2bc
我们令
S
1
−
S
2
S_{1}-S_{2}
S1−S2,则得到
S
1
−
S
2
=
−
2
a
b
−
2
c
d
−
(
−
2
a
d
−
2
b
c
)
=
2
a
d
−
2
a
b
+
2
b
c
−
2
c
d
=
2
a
(
d
−
b
)
+
2
c
(
b
−
d
)
=
2
(
a
−
c
)
(
d
−
b
)
S_{1}-S_{2}=-2ab-2cd-(-2ad-2bc)=2ad-2ab+2bc-2cd=2a(d-b)+2c(b-d)=2(a-c)(d-b)
S1−S2=−2ab−2cd−(−2ad−2bc)=2ad−2ab+2bc−2cd=2a(d−b)+2c(b−d)=2(a−c)(d−b),由于
(
a
−
c
)
≥
0
,
(
d
−
b
)
≥
0
(a-c)\ge 0,(d-b)\ge 0
(a−c)≥0,(d−b)≥0,故
S
1
−
S
2
≥
0
S_{1}-S_{2} \ge 0
S1−S2≥0
由此可知,当这样匹配值之后,结果不会变差,所以这种策略可以使得计算所得的值最大。有归纳法得证。
那么根据这个结论,我们倍增去做,每次判断一下校验值是否小于
T
T
T;由于计算校验值需要保证所维护的序列有序,那么我们可以利用类似归并排序的方法去排序,使得时间复杂度为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
AC代码:
#include<bits/stdc++.h>
#define LL long long
#define N 500005
using namespace std;
int n,m;
LL T;
int a[N],b[N],c[N];
bool check(int l,int r,int p){
for(int i = r + 1;i <= r + p;i ++)
c[i] = a[i];
sort(c + r + 1,c + r + p + 1);
int i = l,j = r + 1,k = l;
while(i <= r && j <= r + p){
if(c[i] < c[j]) b[k ++] = c[i ++];
else b[k ++] = c[j ++];
}
while(i <= r) b[k ++] = c[i ++];
while(j <= r + p) b[k ++] = c[j ++];
LL res = 0 ;
int L = l,R = r + p;
int tmp = m;
while(L < R && tmp){
tmp --;
res += 1LL * (b[R] - b[L]) * (b[R] - b[L]);
R --;L++;
}
return res <= T;
}
void solve(){
scanf("%d%d%lld",&n,&m,&T);
for(int i = 1;i <= n;i ++){
scanf("%d",&a[i]);
}
int ans = 0;
int l = 1,r = 1;
c[1] = a[1];
while(r <= n){
int p = 1;
while(p){
if(r + p <= n && check(l,r,p)){
for(int mh = l;mh <= r + p;mh ++)
c[mh] = b[mh];
r += p,p *= 2;
}
else p >>= 1;
}
ans ++;
r ++;
l = r;
}
printf("%d\n",ans);
}
int main(){
int t;scanf("%d",&t);
while(t --)
solve();
return 0;
}