分治学习笔记
算法思想
分治的主要思想就是分而治之,即把一个大问题分成若干个小问题,先去解决这些小问题,再去解决大问题。分治是一个思想,我们通过一些实际应用来感受一下。
- 归并排序
归并排序是一种稳定的排序算法,最好和最坏时间复杂度均为 ,是一种极其优秀的排序算法,它的原理如下:
假设我们要对一个 长度的数组排序,那么首先我们先把 和 排好序,然后再把两段合到一起就可以整体排好序。而那段数的排序也重复以上过程,知道只剩一个数为止。
把两段已经排好序的数组合并是很简单的,只要维护两个指针就可以在 的时间内排好序。这就是分治,不过它的时间复杂度为什么是 的呢?我们设 表示对 长度数组归并排序的时间复杂度,则会有 ,然后《算法导论》上讲了一个主方法,根据那个方法就可以知道 。不过本人比较菜,不会,于是我给一下章节:
《算法导论》第四章 4.5 用主方法求解递归式
这就是分治的其中一个应用之一,分治能解决很多问题,下面通过以下习题来一一讲述。
一些习题
P1177 【模板】快速排序
题目链接:P1177 【模板】快速排序
思路:
我们可以用归并排序来解决这一问题,归并排序上面已经说过,于是这里给一下代码。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n, a[100005] = {0}, t[100005] = {0};
void slv(int l, int r) {
if (l + 1 == r)
return;
int m = (l + r) / 2;
slv(l, m), slv(m, r);
for (int i = l, j = m, cur = l; i < m || j < r; )
if (i < m && (j == r || a[i] < a[j]))
t[cur++] = a[i++];
else
t[cur++] = a[j++];
for (int i = l; i < r; i++)
a[i] = t[i];
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
slv(1, n + 1);
for (int i = 1; i <= n; i++)
printf("%d ", a[i]);
return 0;
}
P1908 逆序对
题目连接:P1908 逆序对
思路:
这里涉及归并排序一个用处——求逆序对,逆序对是一个序列中满足 且 的 个数。
我们采用分治的思路,考虑对于一个 长度的序列,我们设 。我们先求出 和 两个区间的逆序对个数,并且把这两个区间排好序,然后再求 这个区间的逆序对个数。而那两个区间也是继续一分为二。
我们可以用双指针来求出所有 的逆序对个数。
我们枚举所有的 ,并且维护 指向 中第一个满足 的位置,那么说明 中的所有数都可以和 组成逆序对,于是我们直接把答案加上 即可。
因为 做多移动 次,所以时间复杂度史 的。
我们在求出了逆序对后还要对两端区间进行合并,使得整个区间是有序的,这样才能去求解更大的区间。
时间复杂度和归并排序一样,也是 。
代码:
#include <iostream>
#include <cstdio>
#include <map>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
int n, a[500005] = {0}, t[500005] = {0};
long long slv(int l, int r) {
if (l + 1 == r)
return 0ll;
int m = (l + r) / 2;
long long ans = slv(l, m) + slv(m, r);
for (int i = l, j = m; i < m; i++) {
while (j < r && a[j] < a[i])
j++;
ans += j - m;
}
for (int i = l, j = m, cur = l; i < m || j < r; )
if (i < m && (j == r || a[i] < a[j]))
t[cur++] = a[i++];
else
t[cur++] = a[j++];
for (int i = l; i < r; i++)
a[i] = t[i];
return ans;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
printf("%lld\n", slv(1, n + 1));
return 0;
}
String Reversal
思路:
这道题和 P1966 [NOIP2013 提高组] 火柴排队 几乎完全一样,所以这里只讲一下这道题的做法。
逆序对有一个作用,一个数组的逆序对个数就是这个数组冒泡排序的最少交换次数。我们首先把原字符串的每个字符记作一个二元组,以字符为第一关键字,编号为第二关键字,然后我们再把字符串反转,同样是每个元素记作一个二元组,字符为第一关键字,编号为第二关键字。
我们把两个数组进行从小到大的排序,这时编号相同的元素就是我们要再交换后放在一起的,于是我们新建一个数组 ,对于 ,,然后我们只需要求出 的逆序对个数即可。时间复杂度 。
代码:提交记录
P5094 [USACO04OPEN] MooFest G 加强版
题目链接:P5094 [USACO04OPEN] MooFest G 加强版
思路:
直接暴力复杂度是 的,考虑如何优化。我们没办法去处理最大值和绝对值(),于是我们考虑把听力和坐标变有序,可以用归并。
我们首先对坐标 从小到大排序,这样的话每次归并我们依然是先处理 两个区间内任两头牛的总和( 是中点),再求出每个区间各一只的总和。我们发现,由于我们最开始就按坐标拍好了序,所以对于所有 都有 ,所以我们就不需要去管绝对值了。
于是我们归并时是按照听力的大小从小到大归并,因为外部顺序已经确定,所以内部是可以打乱的。
考虑中间如何求出答案。我们采用双指针的方法,先枚举 ,维护 ,再反过来枚举即可。我们找到最大的 满足 ,此时答案就加上 ,因为 ,所以 ,于是就变成了 ,这个 可以递推,所以每次可以 求出,而 最多移动 次,于是这是 的。
我们现在求出了所有情况中 更大的,再求所有 更大的即可。
时间复杂度是 。
代码:提交记录
P5502 [JSOI2015]最大公约数
思路:
这道题直接枚举时间复杂度是 (不过求 还要加上一个 ),我们考虑如何用分治去求解。
我们依然是先处理出 区间内的答案,然后我们枚举区间的左端点再 内,右端点再 内的权值的最大值。最大公约数可以递推,我们用 的复杂度先预处理出 表示 的最大公约数, 表示 的最大公约数。
最大公约数的个数其实不会超过 个,为啥?因为数越多,最大公约数会单调不升,而每次减少至少一半,所以会有 的最大公约数相同,且是连续的一段,我们枚举 ,然后找到 中所有不同最大公约数中最后一个,去更新答案即可。
时间复杂度是 。
代码:提交记录
P8600 [蓝桥杯 2013 省 B] 连号区间数
题目链接:P8600 [蓝桥杯 2013 省 B] 连号区间数
思路:
这题我们不难发现对于一个区间 , 如果成立,则这个区间就是一个所谓的连号区间。我们依然是分治,在合并时我们分别处理最大值最小值在左右区间的情况,共四种。每一种都用双指针维护即可。
代码:提交记录
P7883 平面最近点对(加强加强版)
题目链接:P7883 平面最近点对(加强加强版)
思路:
我们首先把所有点按照 轴坐标从小到大排序,分治求出左右两个小区间的最近距离,然后考虑如何求出大区间的。
我们设两个小区间内部最近的两个点最小的距离为 ,同时对两个小区间内的点按 轴从小到大排序,然后再对大区间排序。我们选择 轴坐标排序后在中间的那个点,以它的 轴坐标 话一条直线 。我们把所有距离这条直线距离小于 的所有点找出来。因为我们这时是按 轴从小到大排序的,所以选出来的每个点,往后找所有选出的点他们 轴坐标的差的绝对值小于 的,然后跟新答案即可。
这样合并的时间复杂度是 的, 是一个常数,即每个选出的点往后最多有多少个选出的点,他们的 轴坐标差小于 ,一般来说 ,所以可以看成是 的,总的时间复杂度是 的。
代码:提交记录
P3810 【模板】三维偏序(陌上花开)
题目链接:P3810 【模板】三维偏序(陌上花开)
思路:
这题我们可以用 CDQ 分治来解决。我们首先按照 来从小到大排序,然后再按照 从小到大归并排序,在归并的同时,我们来处理答案。
我们在处理完两个小区间的答案后,我们枚举有区间每个元素,维护一个指针指向左区间第一个比有区间当前元素的 大的元素,在移动指针的过程中,如过当前的 比较小,就指针移动,同时用树状数组来维护 ,在 上对应的值加一,这样每次合并就是 的了,总的时间复杂度为 。
代码:提交记录
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异