求逆序对:归并/离散化+树状数组 7.25杭电多校赛 J

10.

Swaps and Inversions

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3588 Accepted Submission(s): 976

 

Problem Description

Long long ago, there was an integer sequence a.Tonyfang think this sequence is messy, so he will count the number of inversions in this sequence. Because he is angry, you will have to pay x yuan for every inversion in the sequence.You don't want to pay too much, so you can try to play some tricks before he sees this sequence. You can pay y yuan to swap any two adjacent elements.What is the minimum amount of money you need to spend?The definition of inversion in this problem is pair (i,j) which 1≤iaj.

 

Input

There are multiple test cases, please read till the end of input file.For each test, in the first line, three integers, n,x,y, n represents the length of the sequence.In the second line, n integers separated by spaces, representing the orginal sequence a.1≤n,x,y≤100000, numbers in the sequence are in [−109,109]. There're 10 test cases.

 

Output

For every test case, a single integer representing minimum money to pay.

 

Sample Input

3 233 666
1 2 3
3 1 666
3 2 1

 

Sample Output

0
3

 

题目大意:

有n个数的数组,其中每有一个逆序对你就要花x元,交换一对数的位置要y元,问最小的花费是多少。

想法:

​ min(x,y)*逆序对个数就是答案。关键就是怎么求逆序对。签到题。

思路:

求逆序对个数有两种方法:归并,离散化+树状数组。

 

归并:

归并排序的合并过程中,逆序交换时,位置前数字的个数就是逆序对个数。它的复杂度是nlogn。

 

离散化+树状数组:

原数组aa[i]记录i这个数字是否出现过,出现过为1。,树状数组维护前缀和,即c[i]=比i小的数的个数,i-getSum(aa[i])即逆序数。按顺序读入aa[],插入树状数组并获取前缀和,ans += i - getSum(aa[i])。

还有个问题就是,i<=1e9,aa[]开不了这么大,所以要离散化。输入值:a[i].val,并记录是第几个输入:a[i].order,然后按val排序,建立映射:aa[a[i].order] = i。

 

标准题解:

注意到逆序对=交换相邻需要交换的次数,那么输出 min(x,y)*逆序对个数 即可。

归并排序:

https://www.cnblogs.com/skywang12345/p/3602369.html

https://www.cnblogs.com/chengxiao/p/6194356.html

树状数组求逆序对:

https://blog.csdn.net/SeasonJoe/article/details/50193789?locationNum=15&fps=1

https://www.cnblogs.com/xiaoningmeng/p/5967693.html

AC代码:

归并

#include 
#include 
#include 
#include 
using namespace std;
typedef long long LL;
const LL N = 100005;
LL a[N], tmp[N];
LL ans;
void Merge(LL l, LL m, LL r) {        //合并两个有序序列
    LL i = l;                       //左指针
    LL j = m + 1;                   //右
    LL k = l;                       //temp要填入位置的指针
    while (i <= m && j <= r) {          //当某一序列没拿完
        if (a[i] > a[j]) {              //谁小谁被填
            tmp[k++] = a[j++];
            ans += m - i + 1;           //出现逆序,逆序对数=该位置前数字个数
        } else {
            tmp[k++] = a[i++];
        }
    }
    while (i <= m)                  //没填入的继续
        tmp[k++] = a[i++];
    while (j <= r)
        tmp[k++] = a[j++];
    for (LL i = l; i <= r; i++)     //把排好序的temp放回原数组
        a[i] = tmp[i];
}
​
void Merge_sort(LL l, LL r) {
    if (l < r) {
        LL m = (l + r) >> 1;
        Merge_sort(l, m);           //“分而”,递归划分区间
        Merge_sort(m + 1, r);
        Merge(l, m, r);             //“治之”,排序再合并区间
    }
}
​
int main() {
    ios::sync_with_stdio(false);    //取消cin同步,加快cin速度
    cin.tie(0);
​
    LL n, x, y;
    while(cin>>n>>x>>y) {
        for (LL i = 0; i < n; i++) {
            cin>>a[i];
        }
        ans = 0;
        Merge_sort(0, n - 1);
        cout<

树状数组:(不知为何WA)

#include 
#include 
#include 
#include 
#include 
using namespace std;
​
const int maxn= 500005;
int aa[maxn];//离散化后的数组
int c[maxn]; //树状数组
int n;
​
struct Node
{
    int v;
    int order;
}a[maxn];
​
bool cmp(Node a, Node b)
{
    return a.v < b.v;
}
​
int lowbit(int k)
{
    return k&(-k); //基本的lowbit函数 
}
​
void update(int t, int value)
{     //即一开始都为0,一个个往上加(+1),
    int i;
    for (i = t; i <= n; i += lowbit(i))
        c[i] += value;  
}
​
int getsum(int t)
{  //即就是求和函数,求前面和多少就是小于它的个数
    int i, sum = 0;
    for (i = t; i >= 1; i -= lowbit(i))
        sum += c[i];
    return sum;
}
​
int main()
{
    int i,x,y;
    while (scanf("%d%d%d", &n,&x,&y), n)
    {
        for (i = 1; i <= n; i++) //离散化
        {
            scanf("%d", &a[i].v);
            a[i].order = i;
        }
        sort(a + 1, a + n + 1,cmp);//从1到n排序,cmp容易忘
        memset(c, 0, sizeof(c));
        for (i = 1; i <= n; i++)
            aa[a[i].order] = i;
        __int64 ans = 0;
        for (i = 1; i <= n; i++)
        {
            update(aa[i], 1);
            ans += i - getsum(aa[i]); //减去小于的数即为大于的数即为逆序数
        }
        printf("%I64d\n", ans*min(x,y));
    }
    return 0;
}

 

参考:

https://www.ideone.com/Wo55gi

你可能感兴趣的:(大二暑假集训)