题目
https://www.luogu.com.cn/problem/P1966
题目分析
1.求两列之间的距离最小值:其实就是要求a 序列第 k 大的元素必须和序列 b 中第k大的元素的位置必须一样(通俗的讲就是aj与bj大的对应大的,小的对应小的,这样相减的平方和才最小)
2.火柴高度范围是2的31次方,如果直接进行运算数组会超,所以这里需要离散化,用id来表示数字大小。
3.现在得到两个id数组,答案就是A序列不动,B序列变为A序列需要与相邻元素交换几次。
4.计算步骤3的方法就是使用一个数组Q,令 q[a[i]] = b[i] ,相当于以 a[i] 为关键字对序列 b[i] 排序。
5.排序移动的次数其实就是序列Q中的逆序对的和。
以题目中的输入一为例:
A: 2 3 1 4
A下标:1 2 3 4
B: 3 2 1 4
B下标:1 2 3 4
再以数字的值排序后:
A: 1 2 3 4
A下标:3 1 2 4
B: 1 2 3 4
B下标:3 2 1 4
令 q[a[i]] = b[i] (这里a[]为A下标,b[]为B下标)
A下标:3 1 2 4
B下标:3 2 1 4
q[3]=3 q[1]=2 q[2]=1 q[4]=4;
对应代码中的:
for (int i = 1; i <= n; i++) q[a[i].id] = b[i].id;
对Q数组的下标整理为从小到大:(这里的思路理解其实是下标:即a[]数组,排序为从小到大,而对应的值:b[]数组也跟着排了,两个数组一起变化,步骤是不变的,也就是对答案不影响,但是a[]数组变得有序了,现在只要让b[]数组有序,计算从此刻开始到有序的步骤既是答案 ,这样做比双方未排序前,直接计算b[]变为a[]需要的步骤更简单可实现)
q[1]=2 q[2]=1 q[3]=3 q[4]=4;
值序列为2 1 3 4
则2 1 3 4的逆序列和即为答案:1
对应代码中的:(这就是逆序对的知识了,不再赘述)
for (int i = 1; i <= n; i++) { update(q[i], 1);//i从小到大的顺序进行update answer += i - getsum(q[i]);//用来存储原数第i个数的order下标是什么 }
代码
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; struct node { long long val; int id; }a[100005],b[100005]; long long c[100005],c2[100005]; int q[100005]; int n; bool cmp(struct node &a, struct node&b) { if (a.val == b.val)return a.id < b.id; return a.val < b.val; } int lowbit(int x) { return x&(-x); } void update(int i, int k) { while (i <= n) { c[i] += k; i += lowbit(i); } } long long getsum(int i) { long long res = 0; while (i > 0) { res += c[i]; i -= lowbit(i); } return res; } int main() { int aa, bb, cc, dd; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%lld", &a[i].val); a[i].id = i; } for (int i = 1; i <= n; i++) { scanf("%lld", &b[i].val); b[i].id = i; } sort(a + 1, a + n + 1, cmp); sort(b + 1, b + n + 1, cmp); for (int i = 1; i <= n; i++) q[a[i].id] = b[i].id; long long answer = 0; for (int i = 1; i <= n; i++) { update(q[i], 1); answer += i - getsum(q[i]);//用来存储原数第i个数的order下标是什么 } printf("%lld ", answer%(99999997)); }
参考:https://blog.csdn.net/m0_38033475/article/details/80330157