2018-07-25 | Contest 2 HDU 6318 Swaps and Inversions 逆序数

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≤i<j≤n and ai>aj.

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


以最少钱数将乱序序列交换恢复字典序

因为两种方法交换方式相同,想要花费最少钱将序列恢复,即:min(x,y)

交换次数 = 逆序数

求出逆序数cnt,min(x,y)*cnt 即是本题答案

 


逆序数的4种求法

(以下12包含cnt,为本题答案)

(仅供于求逆序数)

1.归并排序

稳定排序:归并排序的排序次数就是cnt

#include<bits/stdc++.h>
using namespace std;
long long int cnt;
int a[100010],b[100010];
void Merge(int low,int mid,int high){
	int i=low,j=mid+1,k=0;
	while(i<=mid && j<=high){
		if(a[i]<=a[j]){
			b[k++] = a[i++];
		}
		else {
			b[k++] = a[j++];
			cnt += (mid - i + 1 );///归并两组有序数据,当a[i] > a[j], 则在区间[i, mid]的数据全部大于a[j],此时对于a[j]的逆序数为(mid - i + 1)
		}
	}
	while(i <= mid){
		b[k++] = a[i++];
	}
	while(j <= high){
		b[k++] = a[j++];
	}
	for(k = 0 , i = low ; i<= high ;i++,k++){
		a[i] = b[k];
	}
}
void Merge_sort(int low,int high){
	int mid;
	if(low<high){
		mid=( low + high )/2;
		Merge_sort(low,mid);
		Merge_sort(mid+1,high);
		Merge(low,mid,high);
	}
}//归并排序 
int main(){
	int n,x,y;
	while(~scanf("%d %d %d",&n,&x,&y)){
		for(int i=0;i<n;i++){
			scanf("%d",&a[i]);
		}
		cnt = 0;
		Merge_sort(0,n-1);
		printf("%lld\n",cnt*min(x,y));
	}	
}

2.树状数组

本题数组长度n∈[ 1,100000 ],然而数据范围为 [−10^9,10^9],所以我们需要对数据进行离散化以减小空间使用

  • 用树状数组求逆序对,把数列按照顺序把值插入,比如数列 3 2 1

    1.数列下标为1的值是3,在树状数组3号位置加1,然后用下标减去 getSum(3)

    getSum(3)表示 树状数组中3号位前面有多少个值,这里 getSum(3) = 1 ,就是它自己,但是原数列里它的位置是第一个

    所以 i - getSum(i)  ---->   1 - getSum(3) = 0  所以第一个数产生的逆序对是 0 

    2.数列下标为2的值是2,在2树状数组号位置加1,然后用下标减去 getSum(2)

    树状数组中2号位前面只有它自己,所以 getSum(2) = 1,但是我们知道原数列里面 2号位前面(包括自己)总共是有2个数的

    现在 getSum(2) 只有 1,说明 原数列2号位前面有1个比他大的数,所以 i- getSum(i) = 2 - getSum(2) = 1 ,所以逆序对为1

    3.数列下标为3的值是1,在树状数组1号位加一,然后用下标减去 getSum(1) = 2

    同理得出逆序对为 2 

    所以总的逆序对数为  0 + 1 + 2 = 3

离散化

#include <bits/stdc++.h>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))

#define lowbit(x) (x&(-x))
using namespace std;

const int maxn = 100005;//const 常量 不可变
int n,a[maxn],b[maxn],tree[maxn];

void update(int x,int v){
	while(x <= n){
		tree[x] += v;
		x += lowbit(x);
	}
}

ll getSum(int x){
	ll tmp = 0;
	while(x){
		tmp += tree[x];
		x -= lowbit(x);
	}
	return tmp;
}

void discre(){
	map<int , int>ma;
	sort(b + 1,b + 1 + n);
	int cnt = 1;
	for(int i = 1;i <= n;i ++){
		if(i == 1){
			ma[b[i]] = cnt;
		}
		else{
			if(b[i] != b[i-1]){
				ma[b[i]] = ++cnt;
			}
			else{
				ma[b[i]] = cnt;
			}
		}
	}
	for(int i=1;i<=n;i++){
		a[i] = ma[a[i]];
	}
}
int main(){
	int x,y;
	while(scanf("%d %d %d",&n,&x,&y)!=EOF){
		mem(tree,0);
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			b[i] = a[i];
		}
		discre();
		ll ans = 0;
		for(int i=1;i<=n;i++){
			update(a[i],1);
			ans += i - getSum(a[i]);
		}
		printf("%lld\n",ans*min(x,y));
	}	
	return 0;
}

3.线段树

#include <stdio.h>
 
#define N 100
 
int a[N*2-1];
 
void Inc(int root, int first, int last, int x)
{
    ++a[root];
    if (first == last-1) return;
    if (x < (first+last+1)/2) Inc(root*2+1, first, (first+last+1)/2, x);
    else Inc(root*2+2, (first+last+1)/2, last, x);
}
 
int Sum(int root, int first, int last, int x)
{
    if (first == last-1) return a[root];
    return (x < (first+last+1)/2 ? Sum(root*2+1, first, (first+last+1)/2, x) : 0)
        + Sum(root*2+2, (first+last+1)/2, last, x);
}
 
int main()
{
    int n, res = 0;
    for (scanf("%d", &n); n; --n)
    {
        int t;
        scanf("%d", &t);
        res += Sum(0, 1, N+1, t+1);
        Inc(0, 1, N+1, t);
    }
    printf("%d\n", res);
    return 0;

4.结构体

首先将定义一个结构体,存数列的值和下标,然后按数值从大到小(数值相同按下标从大到小)sort一下

然后建立树状数组,从最大的元素开始,将其标记,即   add(p【i】.id,1)

利用其query(i)查询当前1----i的和,即从1-----i一共有多少个标记的数

对于第i大的数,由于之前所有比它大的数已经标记,所以query(i)就是当前数的逆序数

对于序列的逆序数,只需要加起来就可以了

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define minn(x,y,z) min(min(x,y),z)
struct node
{
    int x,i;
    bool operator < (const node &a) const
    {
        if(x!=a.x)
            return x<a.x;
        return i<a.i;
    }
}p[100005];
int c[100005];
int lowbit(int k)
{
    return k&(-k);
}
 
void add(int k)
{
    while(k < 100005)
    {
        c[k] += 1;
        k += lowbit(k);
    }
}
 
int query(int k)
{
    int sum = 0;
    while(k)
    {
        sum += c[k];
        k -= lowbit(k);
    }
    return sum;
}
 
int main(){
    int n;
    while(~scanf("%d",&n))
    {
        memset(c,0,sizeof(c));
        for(int i = 1; i <= n; i++)
            scanf("%d",&p[i].x),p[i].i=i;
 
        sort(p+1,p+1+n);
 
        ll cnt = 0;
 
        for(int i=n;i>0;i--)
        {
            int ccc =p[i].i;
            cnt = cnt + query(ccc);
            add(ccc);
        }
        printf("%lld\n",cnt);
    }
    return 0;

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值