[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);
}