[bzoj2058][Usaco2010 Nov]Cow Photographs_树状数组_动态规划

Cow Photographs bzoj-2058 Usaco-2010 Nov

题目大意:给定一个n的排列。每次操作可以交换相邻两个数。问将序列变成一个:$i,i+1,i+2,...,n,1,2,...,i-1$形式的序列最少操作次数。

注释:$1\le n\le 10^5$。


想法

我们做过将序列变成1~n的形式,就是用树状数组求一下逆序对个数。

而这个题我们先求出i=1的答案,没错就是逆序对个数。

然后我们考虑如何从$i$变成$i+1$。

每次,我们考虑将当前序列中最小的数变成最大的数加1,再次求这个序列的逆序对个数,就是i+1的答案。

他们之间的关系,就是最小的数后面的数的个数减去前面的数的个数。

理由

我们设f[i]为i开头的答案。

假设我们已经知道了f[i]的答案,即当前序列中的数是$i,i+1,i+2,..,n+i-1$。

这时我们将$i$变成$n+i$,$dic[i]$为i的位置。显然,dic[i]+1到n的数都比n+i小,而1到dic[i]-1的数都比i大,证毕。

最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010 
using namespace std; typedef long long ll;
ll a[N],dic[N]; ll tree[N<<1]; ll n;
inline char nc() {static char *p1,*p2,buf[100000]; return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}
ll rd() {ll x=0; char c=nc(); while(!isdigit(c)) c=nc(); while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=nc(); return x;}
inline ll lowbit(ll x) {return x&(-x);}
void update(ll x) {for(ll i=x;i;i-=lowbit(i)) tree[i]++;}
ll query(ll x) {ll ans=0; for(ll i=x;i<=n+1;i+=lowbit(i)) ans+=tree[i]; return ans;}
int main()
{
	ll now=0,ans=0;
	n=rd(); for(int i=1;i<=n;i++) a[i]=rd(); for(ll i=1;i<=n;i++)
	{
		dic[a[i]]=i; now+=query(a[i]+1);
		update(a[i]);
	}
	ans=now;
	for(int i=1;i<n;i++)
	{
		now+=n+1-2*dic[i];
		ans=min(ans,now);
	}
	printf("%lld\n",ans);
	return 0;
}

小结:好题好题!!

posted @ 2018-09-27 22:46  JZYshuraK_彧  阅读(164)  评论(0编辑  收藏  举报