欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【归并排序】奶牛的图片(jzoj 1812)

程序员文章站 2022-05-10 15:11:55
...

奶牛的图片

jzoj 1812

题目大意

给你一个序列,你可以交换相邻的两个数
让你用最少的交换次数来使得这个序列变成形如a+1,a+2...n,1,2...a1,aa+1,a+2...n,1,2...a-1,a的序列
问你最少的交换次数是多少次

输入样例

5
3
5
4
2
1

输出样例

2

解题思路

如果我们变成1,2...n1,2...n所需次数就是他的逆序对(证明上网找)
如果我们把1放到n后面,那逆序对就要减去1原来的贡献再加上1现在的贡献
就是(si1)+(nsi)-(s_i - 1)+(n-s_i),然后枚举每一种情况,求最小解
注:sis_i是指ii的初始位置

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll n, g, ans, a[100010], b[100010], s[100010];
void work(ll l, ll r)//归并排序求逆序对
{
	if (l >= r) return;
	ll mid = (l + r) / 2;
	ll L = l, R = mid + 1;
	work(l, mid);
	work(mid + 1, r);
	for (ll i = l; i <= r; ++i)
	{
		if ((a[L] < a[R] || R > r) && L <= mid) b[i] = a[L++];
		else 
		{
			b[i] = a[R++];
			g += mid - L + 1;
		}
	}
	for (ll i = l; i <= r; ++i)
		a[i] = b[i];
	return;
}
int main()
{
	scanf("%lld", &n);
	for (ll i = 1; i <= n; ++i)
	{
		scanf("%lld", &a[i]); 
		s[a[i]] = i;
	}
	work(1, n);
	ans = g;
	for (ll i = 1; i < n; ++i)
	{
		g = g - (s[i] - 1) + (n - s[i]);//把当前的第一个数放到最后
		ans = min(ans, g); 
	}
	printf("%lld", ans);
	return 0;
}