火柴排队(归并,逆序对)

火柴排队(归并,逆序对)

给出数组a和b,每个数组有n个数,问如何重排列,使得\(\sum(a_i-b_i)^2\)最大。

首先转换一下式子,发现\((a_i-b_i)^2=a_i^2+b_i^2-2a_ib_i\),因此只要最小化\(a_ib_i\)即可。

有个好东西叫做排序不等式,他告诉我们922029-20180523101131672-1115929931.png
。因此我们只要将a和b都排个序就行了。剩下的工作就是求交换相邻两个数,最少几次能让两个序列相同序号的元素对齐。

如果把\(b_i\)跟着\(a_i\)排序,那么由于\(a_i\)已经变成了升序,序号是1,2,...,n,因此问题又转换成了要交换几步能将\(b_i\)排序。现在只要求\(b_i\)的逆序对就行了。注意必须保持b原有的顺序(至于为什么我也不知道啊qaq)。

#include <cstdio>
#include <algorithm>
#include <functional>
using namespace std;

typedef pair<int, int> pa;
const int maxn=1e5+5, mod=99999997;
int n, arr[maxn], tmp[maxn];
pa a[maxn], b[maxn];

int merge_sort(int *a, int beg, int end){
    if (beg+1==end) return 0;
    int mid=beg+end>>1, i=beg, j=mid, ans=0, cnt=beg;
    ans=merge_sort(a, beg, mid)+merge_sort(a, mid, end); ans%=mod;
    while (i<mid&&j<end){  //统计以j为后一个数的逆序对个数
        if (a[i]>a[j]) ans=(ans+mid-i)%mod, tmp[cnt++]=a[j++];  //为了避免i=mid,j=end必须else一下
        else tmp[cnt++]=a[i++];
    }
    while (i<mid) tmp[cnt++]=a[i++];
    while (j<end) tmp[cnt++]=a[j++];
    for (int i=beg; i<end; ++i) a[i]=tmp[i];
    return ans%mod;
}

int main(){
    scanf("%d", &n); int t;
    for (int i=0; i<n; ++i){ scanf("%d", &t); a[i]=make_pair(t, i); }
    for (int i=0; i<n; ++i){ scanf("%d", &t); b[i]=make_pair(t, i); }
    sort(a, a+n); sort(b, b+n);
    for (int i=0; i<n; ++i) arr[a[i].second]=b[i].second;
    printf("%d\n", merge_sort(arr, 0, n));
    return 0;
}

转载于:https://www.cnblogs.com/MyNameIsPc/p/9075504.html

### 使用 C++归并排序算法计算数组中的逆序对数量 要使用归并排序算法计算数组中的逆序对数量,可以基于归并排序的思想,在合并两个子数组的过程中统计跨组的逆序对。以下是详细的实现方式: #### 实现逻辑 1. **分治法**:将数组分为两部分 `[l, mid]` 和 `[mid+1, r]`,分别递归地计算这两部分内部的逆序对。 2. **合并阶段**:在合并过程中,当左侧子数组的一个元素大于右侧子数组的一个元素时,则说明存在逆序对。具体来说,如果 `arr[i] > arr[j]` (其中 `i ∈ [l, mid], j ∈ [mid+1, r]`),那么从索引 `i` 到 `mid` 的所有元素都会与当前的 `j` 构成逆序对。 #### 代码实现 以下是一个完整的 C++ 实现示例: ```cpp #include <iostream> #include <vector> using namespace std; long long mergeAndCount(vector<int>& nums, vector<int>& temp, int l, int m, int r) { int i = l; // 左侧子数组起始位置 int j = m + 1; // 右侧子数组起始位置 int k = l; // 合并后的数组起始位置 long long count = 0; while (i <= m && j <= r) { if (nums[i] <= nums[j]) { // 如果左侧小于等于右侧,无逆序对 temp[k++] = nums[i++]; } else { // 如果左侧大于右侧,构成逆序对 temp[k++] = nums[j++]; count += (m - i + 1); // 计算跨越的逆序对数目 } } // 复制剩余的左半部分 while (i <= m) { temp[k++] = nums[i++]; } // 复制剩余的右半部分 while (j <= r) { temp[k++] = nums[j++]; } // 将临时数组的内容复制回原数组 for (int p = l; p <= r; ++p) { nums[p] = temp[p]; } return count; } long long mergeSortAndCount(vector<int>& nums, vector<int>& temp, int l, int r) { long long count = 0; if (l < r) { int m = l + (r - l) / 2; count += mergeSortAndCount(nums, temp, l, m); count += mergeSortAndCount(nums, temp, m + 1, r); count += mergeAndCount(nums, temp, l, m, r); } return count; } long long inversePairs(vector<int> nums) { int n = nums.size(); vector<int> temp(n, 0); return mergeSortAndCount(nums, temp, 0, n - 1); } int main() { vector<int> nums = {7, 5, 6, 4}; cout << "Number of Inverse Pairs: " << inversePairs(nums) << endl; return 0; } ``` #### 关键点解析 - 在函数 `mergeAndCount` 中,每当发现 `nums[i] > nums[j]` 时,会增加 `(m - i + 1)` 对逆序对[^1]。 - 函数 `mergeSortAndCount` 是递归调用的核心,负责分解问题并对每层的结果进行累加。 - 整体的时间复杂度为 \(O(n \log n)\),空间复杂度为 \(O(n)\)[^3]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值