梗概
倍增算法,顾名思义,就是不断地翻倍。
虽然是一种基础算法,但它能够使得线性的处理转化为对数级的处理,大大地优化时间复杂度,在很多算法中都有应用,其中最常见的就是ST表以及LCA了。
一些使用到倍增的算法、思想
- 快速幂(求大指数幂,矩阵快速求幂)
- ST表(处理区间可合并的查询(区间最大值,区间最小值))(搞清楚怎么转移,以便于化用到多维情况)
- LCA(找最近公共祖先)
- 路径转移倍增(求连续转移m次后的位置)
- [思想] 压缩有效范围
一些例题
快速幂
这个还是算了,太基础了,我放个模版题上来算了:
ST表
P2880 [USACO07JAN] Balanced Lineup G
P6648 [CCC2019] Triangle: The Data Structure
LCA
暂时不知道有啥,找到了我再多放几个上来
路径转移倍增
P7562 [JOISC 2021 Day4] イベント巡り 2 (Event Hopping 2)
(个人觉得这题 ↑ 会比下面那题稍微难一点)
[思想] 压缩有效范围
思路
1
2
3
4
5
6
7求最小值的最大值,一眼二分,可以二分一下右端点,但是考虑如果每一段长度都为
1,那么每次都要二分 log 次,总的是 O(n^2 log n),显然也不行。
发现复杂度瓶颈在于每次二分得到的区间长是 O(n) 级的,那么可不可以对于一个左端
点,快速的找到右端点的范围呢?我们考虑倍增,设左端点为 l,我们倍增求出右端点
的范围。设倍增得到最大的满足条件的区间长度为 2k,那么我们二分的左端点为
i+2k−1,右端点为 min(n,i+2k+1−1),在这段区间内二分,显然复杂度就正确了。
参考代码
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309#include<bits/stdc++.h>
using i64 = long long;
using u64 = unsigned long long;
using ld = double;
std::mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
ld eps = 1e-13 * (rng() % 10000 + 1);
template <class T, class G>
struct BaseVector2 {
T x, y;
constexpr BaseVector2() : BaseVector2(T{}, T{}) {}
constexpr BaseVector2(T x, T y) : x{x}, y{y} {}
// 运算
constexpr BaseVector2 operator+(BaseVector2 a) const {
return BaseVector2(x + a.x, y + a.y);
}
constexpr BaseVector2 operator-(BaseVector2 a) const {
return BaseVector2(x - a.x, y - a.y);
}
constexpr BaseVector2 operator-() const {
return BaseVector2(-x, -y);
}
constexpr G operator*(BaseVector2 a) const {
return G(x) * a.x + G(y) * a.y;
}
constexpr G operator%(BaseVector2 a) const {
return G(x) * a.y - G(y) * a.x;
}
constexpr BaseVector2 rotate() const {
return BaseVector2(-y, x);
}
template<class Float>
constexpr BaseVector2 rotate(Float theta) const {
BaseVector2 b(cosl(theta), sinl(theta));
return BaseVector2(x * b.x - y * b.y,
x * b.y + y * b.x);
}
constexpr friend BaseVector2 operator*(const T &a, BaseVector2 b) {
return BaseVector2(a * b.x, a * b.y);
}
// 比较
constexpr bool operator<(BaseVector2 a) const {
if (x == a.x) {
return y < a.y;
}
return x < a.x;
}
constexpr bool operator>(BaseVector2 a) const {
if (x == a.x) {
return y > a.y;
}
return x > a.x;
}
constexpr bool operator<=(BaseVector2 a) const {
if (x == a.x) {
return y <= a.y;
}
return x <= a.x;
}
constexpr bool operator>=(BaseVector2 a) const {
if (x == a.x) {
return y >= a.y;
}
return x >= a.x;
}
constexpr bool operator==(BaseVector2 a) const {
return x == a.x and y == a.y;
}
constexpr bool operator!=(BaseVector2 a) const {
return x != a.x or y != a.y;
}
// 输入输出
friend std::istream &operator>>(std::istream &in, BaseVector2 &p) {
return in >> p.x >> p.y;
}
friend std::ostream &operator<<(std::ostream &ot, BaseVector2 p) {
return ot << '(' << p.x << ", " << p.y << ')';
}
};
template <class T, class G>
G dis2(BaseVector2<T, G> a, BaseVector2<T, G> b = BaseVector2<T, G>(0, 0)) {
BaseVector2<T, G> p = a - b;
return p * p;
}
template <class T, class G>
auto dis(BaseVector2<T, G> a, BaseVector2<T, G> b = BaseVector2<T, G>(0, 0)) {
return sqrtl(dis2(a, b));
}
template <class T, class G>
int sgn(BaseVector2<T, G> p) {
if (p.x < 0 or p.x == 0 and p.y > 0) {
return 1;
} else
return 0;
// 以41象限为先
}
template <class Vector>
bool polarCmp(Vector p, Vector q) {
if (sgn(p) == sgn(q)) {
if (p % q == 0) {
return dis2(p) < dis2(q);
} else {
return p % q > 0;
}
} else {
return sgn(p) < sgn(q);
}
}
using Point = BaseVector2<ld, ld>;
using Vector = Point;
using PS = std::vector<Point>;
// Line a-b cross Line c-d (here Point = Point<ld, ld>)
Point cross(Point a, Point b, Point c, Point d) {
Point v = c - d;
ld sa = v % (a - d), sb = (b - d) % v;
return 1.L * sa / (sa + sb) * (b - a) + a;
}
const ld pi = acosl(-1);
// a-b 和 a-c 的夹角 signed
ld getAngle(Point a, Point b, Point c) {
auto v1 = b - a, v2 = c - a;
return atan2l(v1 % v2, v1 * v2); // ∠bac
}
int sgn(const ld &a) {
return (a < -eps ? -1 : a > eps);
}
// 对四个不同的点判断四点共圆
// d在abc外接圆外return 1, 内return -1
int inCircle(Point a, Point b, Point c, Point d) {
ld ag1 = getAngle(a, b, c), ag2 = getAngle(d, c, b);
if (sgn(ag1) == sgn(ag2)) {
return sgn(pi - std::abs(ag1 + ag2));
} else {
return sgn(std::abs(ag1) - std::abs(ag2));
}
}
//using pii = std::pair<int, int>;
Point excir(const Point &a, const Point &b, const Point &c) {
// 三点求外接圆 圆心
auto d1 = 0.5 * (a + b), d2 = d1 + (a - b).rotate();
auto d3 = 0.5 * (c + b), d4 = d3 + (c - b).rotate();
return cross(d1, d2, d3, d4);
}
struct MinCircleOver {
Point Center;
ld Radius = 0;//?
MinCircleOver(PS ps) {
std::shuffle(begin(ps), end(ps), std::mt19937(time(0)));
for (int i = 0; i < ps.size(); i++) {
if (sgn(dis(Center, ps[i]) - Radius - eps) <= 0)
continue;
// i点不在圆内,说明 i要更新圆
Center = ps[i], Radius = 0;
for (int j = 0; j < i; j++) {
if (sgn(dis(Center, ps[j]) - Radius - eps) <= 0)
continue;
// j不在圆内,说明 j要更新圆
Center = 0.5 * (ps[i] + ps[j]), Radius = dis(Center, ps[i]);
for (int k = 0; k < j; k++) {
if (sgn(dis(Center, ps[k]) - Radius - eps) <= 0)
continue;
// k不在圆内,说明 k要更新圆
Center = excir(ps[i], ps[j], ps[k]), Radius = dis(Center, ps[i]);
}
}
}
}
};
PS ps(100000), rps(100000);
Point Center;
ld Radius;
void FastMCO(int l, int r) {//卡常技巧:手动随机化
int cnt = 0;
for (int i = l; i <= r; i++) rps[cnt++] = ps[i];
for (int i = 0; i < cnt; i++) std::swap(rps[i], rps[rng() % cnt]);
Center = rps[0];
Radius = 0;
for (int i = 0; i < cnt; i++) {
if (sgn(dis(Center, rps[i]) - Radius - eps) <= 0)
continue;
// i点不在圆内,说明 i要更新圆
Center = rps[i], Radius = 0;
for (int j = 0; j < i; j++) {
if (sgn(dis(Center, rps[j]) - Radius - eps) <= 0)
continue;
// j不在圆内,说明 j要更新圆
Center = 0.5 * (rps[i] + rps[j]), Radius = dis(Center, rps[i]);
for (int k = 0; k < j; k++) {
if (sgn(dis(Center, rps[k]) - Radius - eps) <= 0)
continue;
// k不在圆内,说明 k要更新圆
Center = excir(rps[i], rps[j], rps[k]), Radius = dis(Center, rps[i]);
}
}
}
}
void solve() {
int n, m;
std::cin >> n >> m;
// PS ps(n);
// for (auto &[x, y] : ps) {
// std::cin >> x >> y;
// }
for (int i = 0; i < n; i++) {
std::cin >> ps[i];
}
std::vector<std::array<int, 2>> res(m);
int m_ = 0;
auto ck = [&](ld R) {
m_ = 0;
int ans = 0;
for (int i = 0; i < n; i = ans + 1) {
int k;
for (k = 1; i + (1 << k) < n; k++) { //倍增压缩范围
//PS ps_(ps.begin() + i, ps.begin() + i + (1 << k) + 1);
//MinCircleOver get(ps_);
FastMCO(i, i + (1 << k));
if (Radius > R + eps) break;
}
ans = i;
int l = i + (1 << (k - 1)), r = std::min(n - 1, i + (1 << k));
while (l <= r) { //二分
int mid = (l + r) / 2;
//PS ps_(ps.begin() + i, ps.begin() + mid + 1);
//MinCircleOver get(ps_);
FastMCO(i, mid);
if (Radius < R + eps) {
l = mid + 1;
ans = mid;
} else {
r = mid - 1;
}
}
res[m_][0] = i;
res[m_++][1] = ans;
if (m_ > m) return 0;
}
return 1;
};
//MinCircleOver init(ps);
FastMCO(0, n - 1);
ld l = 0, r = Radius;
if (m > 1) {
int cnt = 50;//二分上限50次
while (cnt-- && r - l > eps) {
ld mid = (l + r) / 2;
if (ck(mid)) r = mid;
else l = mid;
}
ck(r);
std::cout << r << "\n";
std::cout << m_ << "\n";
for (int i = 0; i < m_; i++) {
//PS ps_(ps.begin() + res[i][0], ps.begin() + res[i][1] + 1);
//MinCircleOver mco(ps_);
FastMCO(res[i][0], res[i][1]);
std::cout << Center.x << " " << Center.y << "\n";
}
} else {
std::cout << Radius << "\n";
std::cout << "1\n";
std::cout << Center.x << " " << Center.y;
}
}
signed main(){
std::cin.tie(0) -> sync_with_stdio(false);
int t = 1;
//std::cin >> t;
std::cout << std::setprecision(8) << std::fixed;
while(t--) solve();
return 0;
}