HDU 6318 Swaps and Inversions

2018 Multi-University Training Contest 2 1010 

Swaps and Inversions

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

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

Source

2018 Multi-University Training Contest 2

Recommend

chendu


大致题意及其:给你一串数组,每次交换前后两个数操作花费y元,最后剩下多少逆序对你就要支付x元,问你你恢复升序排列最少需要多少前。刚开始自己出样例模拟了一遍,发现每次交换操作最多使逆序对-1,而且在恢复升序前都能找到可以交换的操作使逆序对减1;所以问题就单纯的变成了求逆序对的问题,于是上网找了个求逆序对的模板交了一发,发现wa了,改long long 就过了。

求逆序对有两种方法:

1.离散化加树状数组:

几个月前就学了树状数组,数据结构与算法分析课上要求要求逆序对,于是百度了一发,发现树状数组竟然可以用来求,然而一直没弄明白他是怎么实现的,今天学长讲了才略懂。

逆序对就是如果i > j && a[i] < a[j],这两个就算一对逆序对,简单来说,所有逆序对的个数和就是找每一个数的前面有几个比他的大的数,他们加起来的和就是逆序对的总数。

知道什么是逆序对后就好办了,树状数组的功能就是可以单点更新,区间查询,这样你把每个数该在的位置离散化出来,然后每次把每个数该在的位置上加上1,比如一个数该在的位置为x,那么用add(x)把这个位置加上1,然后再用区间查询read(x)查询1~x的和,也就是可以知道前面有多少个数是比他小的了(包括那个数自己),再用已经插入的数的个数减去read(x),就算出了前面有多少个数比他大了。

附上代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
 
const int maxn = 100005;
 
#define ll long long
#define lowbit(x) (x & (-x))
#define mem(a,x) memset(a,x,sizeof(a))
 
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;
}

2.归并排序求逆序对:

这个在数据结构与算法分析课老师讲过,比较容易,这里就不说那么多了。附上学长的代码;

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include <list>
#define INF 0x3f3f3f3f
#define maxn 105000
#define maxnn 6000
#define juzheng 300
#define line cout << "-------------------------" << endl;
#define PI acos(-1.0)
#define mem(a,b) memset(a,b,sizeof(a))
#define fill_(a,b,n) fill(a,a + n,b)
#define esp 1e-9

#define ri(n) scanf("%d",&n)
#define ri2(a,b) scanf("%d %d",&a,&b)
#define ri3(a,b,c) scanf("%d %d %d",&a,&b,&c)
#define rd(n) scanf("%lf",&n)
#define rd2(a,b) scanf("%lf %lf",&a,&b)
#define rd3(a,b,c) scanf("%lf %lf %lf",&a,&b,&c)
#define rl(n) scanf("%lld",&n)
#define pr(n) cout << n << endl
#define ll long long
#define int64 __int64

using namespace std;

ll a[100005];
ll n;
ll ans;//好吧,会爆int

ll tmp[100005];

void merge_sort(ll s, ll t) {
    if(s == t) return;
    ll mid = (s + t) >> 1;
    merge_sort(s, mid);
    merge_sort(mid + 1, t);

    ll cnt = 0;
    ll i, j;
    for(i = s, j = mid + 1; i <= mid && j <= t; ) {
        if(a[i] > a[j]) {
            tmp[cnt ++] = a[j];
            j ++;
            ans += (mid - i + 1);
        } else {
            tmp[cnt ++] = a[i];
            i ++;
        }
    }
    while(i <= mid) {
        tmp[cnt ++] = a[i ++];
    }
    while(j <= t) {
        tmp[cnt ++] = a[j ++];
    }
    for(ll i = s, j = 0; i <= t && j <= cnt; i ++, j ++) {
        a[i] = tmp[j];
    }
}

ll x,y;

int main() {
    while(~rl(n)) {
        scanf("%lld %lld",&x,&y);

        for(ll i = 1; i <= n; i ++) {
            scanf("%lld", &a[i]);
        }
        
        ans = 0;
        merge_sort(1, n);
        printf("%lld\n", min(x,y) * ans);//注意这里用%I64d会PE,或者用cout也行
        //cout << ans << endl
    }

    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值