1812. 【Usaco 2010 NOV Gold】奶牛的图片

Description

Farmer John希望给他的N(1<=N<=100,000)只奶牛拍照片,这样他就可以向他的朋友炫耀他的奶牛.这N只奶牛被标号为1..N.
在照相的那一天,奶牛们排成了一排.其中第i个位置上是标号为c_i(1<=c_i<=N)的奶牛.对于奶牛的站位,Farmer John有他自己的想法.
FJ是这么想的,标号为i(1<=i<=n-1)的奶牛只能站在标号为i+1的奶牛的左边,而标号为N的奶牛只能站在标号为1的奶牛的左边.当然,没有牛可以站在队列中最左边的奶牛的左边了.也就是说,最左边的奶牛编号是随意的.
这些奶牛都非常的饿,急切的希望吃到FJ承诺的在拍照后的大餐,所以FJ想尽快的拍照.奶牛们的方向感非常的不好,所以FJ每一分钟只可以选择相邻的两只奶牛然后让他们交换位置.FJ最小需要多少时间就能使奶牛站成一个可以接受的序列?
比方说一个有5只奶牛的例子,一开始序列是这样的:
左边 右边
3 5 4 2 1

第一分钟,FJ可以交换第二队奶牛(即5和4),交换后的队列:
3 4 5 2 1
第二分钟,FJ交换最右边的一对,序列变成这样:
3 4 5 1 2
这样,只用了2分钟,就是序列变为了一个FJ所希望的序列.

Input

第1行:一个单独的数N
第2到n+1行:第i+1行上的数表示站在第i的位置上的奶牛的编号(即c_i).

Output

一个整数,表示是奶牛的序列变为一个合法的序列的最小花费时间.

Sample Input

5
3
5
4
2
1

Sample Output

2

Solution

题目意思是左边的第一个(序列是连续的)。

对于每一个点变回1~n的有序数列的最小操作数是整个序列的逆序对的个数。

枚举最终变成的数列的状态

可以将最终状态在原数组中的印射找出来,即1在原数组第几个位置,那么这个印射数组的逆序对数就是原数组转化为最终状态的数组的最少变化量(也等于原数组中的总逆序对数),因为相当于把最终状态倒回原数组的位置,那么就需要移动以当前点为结尾的逆序对数,总和即为总逆序对数。最终状态的数列在变,但是对应的印射是不变的。

因为对于一个数列一定是由上一个枚举的数列的第一位调到最后一位得来的,

那么开头的数放到最后面的变化量:

逆序对减少:开头的数的值-1(因为在开头时后面有1~a[ i ]-1)

逆序对增加:n-开头的数的值,(因为放到最后就有a[ i ]+1~n比它大)

O( 1 )即可计算出逆序对的变化,那么O(n)扫一遍对于每一状态取min即可。

Code

#include<cstdio> 
#include<cstring>
#include<algorithm>
#define I int
#define ll long long
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define N 100010 
using namespace std;
I n,x,a[N];
ll c[N],now=0,ans;
void add(I x){
	for(;x<=n;x+=x&-x) c[x]++;
}
ll ask(I x){
	if(!x) return 0;
	ll s=0;
	for(;x;x-=x&-x) s+=c[x];
	return s;
}
I main(){
	scanf("%d",&n);
	F(i,1,n){
		scanf("%d",&x);
		add(x);
		now+=i-1-ask(x-1);
		a[x]=i;
	}
	ans=now;
	F(i,1,n-1){
		now=now-a[i]+1+n-a[i];
		ans=min(ans,now);
	}
	printf("%lld\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zsjzliziyang/article/details/107880774
今日推荐