归并分治
[Algo] 归并分治
1. 经典归并排序
// 1. 经典归并排序
void merge(vector<int> &v, int left, int mid, int right)
{
vector<int> tmp(v);
int i = left, j = mid + 1, k = left;
while (i <= mid && j <= right) tmp[k++] = v[i] <= v[j] ? v[i++] : v[j++];
while (i <= mid) tmp[k++] = v[i++];
while (j <= right) tmp[k++] = v[j++];
v = tmp;
}
void mergeSort(vector<int> &v, int left, int right)
{
if (left == right) return;
int mid = (left + right) / 2;
mergeSort(v, left, mid);
mergeSort(v, mid + 1, right);
merge(v, left, mid, right);
}
2. 归并分治
1)思考一个问题在大范围上的答案,是否等于,左部分的答案 + 右部分的答案 + 跨越左右产生的答案
2)计算“跨越左右产生的答案”时,如果加上左、右各自有序这个设定,会不会获得计算的便利性
3)如果以上两点都成立,那么该问题很可能被归并分治解决(话不说满,因为总有很毒的出题人)
小和问题:
// 2. 小和问题
long crossing(vector<int> &v, int left, int mid, int right)
{
// 统计
long ans = 0;
for (int j = mid + 1, i = left, sum = 0; j <= right; j++)
{
while (i <= mid && v[i] <= v[j]) sum += v[i++];
ans += sum;
}
// merge
vector<int> tmp(v);
int i = left, j = mid + 1, k = left;
while (i <= mid && j <= right) tmp[k++] = v[i] <= v[j] ? v[i++] : v[j++];
while (i <= mid) tmp[k++] = v[i++];
while (j <= right) tmp[k++] = v[j++];
v = tmp;
return ans;
}
long smallSum(vector<int> &v, int left, int right)
{
if (left == right) return 0;
int mid = (left + right) / 2;
return smallSum(v, left, mid) + smallSum(v, mid + 1, right) + crossing(v, left, mid, right);
}