先贴一个TLE的代码...
#include<iostream>
#include<cstdio>
#define MAXN 11111
using namespace std;
int gt[MAXN<<2],st[MAXN<<2];
int date[MAXN];
void pushUpmax( int rt ){
gt[rt]=max(gt[rt<<1],gt[rt<<1|1]);
}
void pushUpmin( int rt ){
st[rt]=min(st[rt<<1],st[rt<<1|1]);
}
void build( int l,int r,int rt )
{
if( l==r )
{
gt[rt]=st[rt]=date[l];
return ;
}
int m=(l+r)/2;
build( l,m,rt<<1 );
build( m+1,r,rt<<1|1 );
pushUpmax(rt);
pushUpmin(rt);
}
int querygt( int v,int l,int r,int rt )//在最大值树上查找[l,r]区间内小于v的节点数。
{
if( l==r )
return 0;
if( gt[rt]<v )
return r-l+1;
int m=(l+r)/2;
int ret=0;
ret+=querygt( v,l,m,rt<<1 );
ret+=querygt( v,m+1,r,rt<<1|1 );
return ret;
}
int queryst( int v,int l,int r,int rt )//在最小值树上查找[l,r]区间内大于v的节点数
{
if( l==r )
return 0;
if( gt[rt]>v )
return r-l+1;
int m=(l+r)/2;
int ret=0;
ret+=queryst( v,l,m,rt<<1 );
ret+=queryst( v,m+1,r,rt<<1|1 );
return ret;
}
int main()
{
int N;
while( scanf("%d",&N)!=EOF )
{
for( int i=1;i<=N;i++ )
{
scanf( "%d",&date[i] );
date[i+N]=date[i];
}
build(1,2*N,1);
int ans=0;
for( int i=1;i<=N;i++ )
for( int j=i+1;j<=N;j++ )
if( date[i]>date[j] )
ans++;
for( int i=1;i<=N;i++ )
ans=min( ans,ans+queryst(date[i],i+1,i+N-1,1)-querygt(date[i],i+1,i+N-1,1) );
printf( "%d\n",ans );
}
return 0;
}
虽然TLE了但是是我的一次尝试。虽然TLE了... 无奈..
首先定义逆序数:
在一串数字序列中i<j时,Ai>Aj的个数。反之,i>j时,Ai<Aj的个数.
通俗的讲就是Ai之前比Ai大的的数字个数.
下面简要叙述一下我的公式吧。
|.......|AB|......|
假设AB是串中的两个临近的数,将这两个数换位置,逆序数会如何改变呢?
|.......|BA|......|
if(A>B) r--;
if(B>A) r++;
显然AB的换位和左右两边这些个东西: |......| 是没有关系的。
那么A|......|换位成为|......|A呢?
我们可以分步看...
ABCDEFG
BACDEFG
BCADEFG
BCDAEFG
BCDEAFG
BCDEFAG
BCDEFGA
这样将A移到最后。
那么这个过程的逆序数是怎样变化的呢?
简单推知A增加的逆序数为[B,G]中大于A的。A减少的逆序数为[B,G]中小于A的。
so... R+=[B,G]中大于A的-[B,G]中小于A的。
R为原逆序数。
这就是上面的代码的由来...
很可惜,TLE了...
在题目中有特殊性质。数字有N个为[0,N-1]各有一个。
于是乎将Ai移到最后,其中大于Ai的有(N-Ai-1)个,小于Ai的有Ai个,于是
R+=(N-Ai-1)-Ai;
这便是一次变化的公式。
so...
暴力来做吧...
#include<iostream>
using namespace std;
int main()
{
int N;
while( scanf( "%d",&N )!=EOF )
{
int date[5555],ans=0;
for( int i=0;i<N;i++ )
scanf( "%d",&date[i] );
for( int i=0;i<N;i++ )
for( int j=0;j<i;j++ )
if( date[j]>date[i] )
ans++;
int temp=ans;
for( int i=0;i<N;i++ )
{
temp=temp-date[i]+(N-date[i]-1);
ans=min( ans,temp );
}
printf("%d\n",ans );
}
return 0;
}
树状数组:
#include<iostream>
using namespace std;
int tree[5555],N;
void update( int pt,int v )
{
while( pt<=N )
{
tree[pt]+=v;
pt+=pt&(-pt);
}
}
int find( int pt )
{
int ret=0;
while( pt )
{
ret+=tree[pt];
pt-=pt&(-pt);
}
return ret;
}
int main()
{
int date[5555];
while( scanf("%d",&N)!=EOF )
{
memset( tree,0,sizeof(tree) );
for( int i=0;i<N;i++ )
{
scanf( "%d",&date[i] );
date[i]++;
}
int ans=0;
for( int i=N-1;i>=0;i-- )
{
ans+=find(date[i]);
update(date[i],1);
}
printf( "%d\n",ans );
int temp=ans;
for( int i=0;i<N;i++ )
{
date[i]--;
temp=temp-date[i]+(N-date[i]-1);
ans=min(temp,ans);
}
printf( "%d\n",ans );
}
return 0;
}