树状数组 P1966 火柴排队【逆序对】

题目

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\n", answer%(99999997));
}

 

参考:https://blog.csdn.net/m0_38033475/article/details/80330157

posted @ 2020-05-04 21:55  Jason66661010  阅读(112)  评论(0编辑  收藏  举报