刷题记录:牛客NC24748[USACO 2010 Nov G]Cow Photographs

传送门:牛客

题目描述

Farmer John wants to take a picture of his entire herd of N (1 <= N <= 100,000) cows conveniently 
numbered 1..N so he can show off to his friends.
On picture day, the cows run to form a single line in some arbitrary order with position i containing cow 
  <= N). Farmer John has his own ideas about how the cows should line up.
FJ thinks cow i may stand only to the left of cow i+1 (for all i, 1 <= i <= N-1) and that cow N may only 
stand to the left of Cow 1. Of course, no cow will stand to the left of the first (leftmost) cow in the line.
The cows are hungry for the promised post-photo dinner, so Farmer John wants to take the picture as 
quickly as possible. Cows are not great at following directions, so he will only choose a pair of adjacent 
cows and have them switch places once per minute. How quickly is Farmer John able to get them into 
some acceptable order?
输入:
5 
3 
5 
4 
2 
1 
输出:
2

本题是一道数学题,使用线段树来维护逆序对

来举一个栗子来讲讲这道题怎么跟逆序对产生关系

比如3,5,4,2,1我们想将其改为一个可以接受的序列,对于一个可以接收的序列,我们发现有这几种,1,2,3,4,5 ,2,3,4,5,1,3,4,5,1,2,4,5,1,2,3,5,1,2,3,4这几种,我们先来想一下,对于转化成第一种序列我们需要几个步骤,我们会发现显然是该序列中的逆序对个数,对于将一个序列排好序的步骤显然是这个序列的逆序对的个数
那么对于转化成第二种序列我们需要几个步骤呢,此时我们发现和第一种不同,此时我们不再是一个排好序的序列了.此时我们有一个解决办法就是将原序列中的1看做6,然后此时我们会发现最后的可接受序列又变成了一个排好序的序列,此时我们的答案就又是逆序对的个数了.

对于其他序列我们都可以使用这个方法来解决.对于解决逆序对问题来说,我们需要 n l o g n nlogn nlogn的复杂度,对于我们的序列个数来说,又有 n n n个,此时我们的复杂度达到了 n 2 l o g n n^2logn n2logn,显然此时无法通过此题,所以我们得需要一点点的优化.

对于第二种序列来说,此时我们将1改变成了6,那么此时我们看一下对于逆序对来说发生了什么变化,我们发现对于此时改变的这个数字来说,显然当前的数字6是最大的,并且原来的数字1是最小的,所以此时我们发现对于原本的序列来说,此时我们的序列中的逆序对的个数减少了 p o s ( 1 ) − 1 pos(1)-1 pos(1)1,增加了 n − p o s ( 1 ) n-pos(1) npos(1).所以此时我们可以根据原来的逆序对个数来计算出此时的逆序对个数.

那么对于第三种序列来说,此时我们可以根据第二种序列来进行进一步操作,此时我们已经可以计算出第二种序列的个数了,也就是此时3,5,4,2,6的步骤已经算出来了,对于此时的第三种序列来说,我们此时需要计算出来的是3,5,4,7,6的逆序对个数,那么此时显然就是在第二种序列的情况下,继续进行之前的改变即可


下面是具体的代码部分:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define int long long
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int tree[maxn],n;
int lowbit(int x) {
	return x&(~x+1);
}
void Add(int pos,int val) {
	while(pos<=n) {
		tree[pos]+=val;
		pos+=lowbit(pos);
	}
}
int query(int pos) {
	int ans=0;
	while(pos) {
		ans+=tree[pos];
		pos-=lowbit(pos);
	}	
	return ans;
}
int a[maxn];int pos[maxn];
signed main() {
	n=read();
	for(int i=1;i<=n;i++) {
		a[i]=read();
		pos[a[i]]=i;
	}
	int sum=0;
	for(int i=1;i<=n;i++) {
		Add(a[i],1);
		sum+=query(n)-query(a[i]);
	}
	int ans=ll_INF;
	for(int i=1;i<=n;i++) {
		sum=sum-(pos[i]-1)+n-pos[i];
		ans=min(ans,sum);
	}
	cout<<ans<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值