题目大意
链接:https://vjudge.net/problem/HDU-1394
意思就是说:给一个整数n,后面一行给出0~n-1的数的排列,可以对这个排列作一下变换:把第一个数放到最后一个数的位置生成新的排列,求在所有能生成的排列中,逆序数的最小值是多少。
思路分析
听说可以不用线段树?但是我看到涉及区间和查找的就先用线段树试了试。思路就是:每输入一个数,就在线段树上记下这个有这个数ai(在树的最底层对用位置将这个位置的值设为1),之后就可以像普通的线段树一样pushup向上传递。之后,查找目前树中范围在ai~n范围内的数的个数就行了。
但这道题有个bug,不知道是数据太水没测出来还是我有什么没有想通的。我们平时线段树存数据都是从1开始的,而这道题中含有0,这个0会被合并到1的位置,导致sum[1所占的位置]=2,感觉这样不大好,会干扰后面的判断,所以我把每个数都+1,当作所给的数是1~n范围的数据防止冲突,也过了oj,不知会不会有什么不妥,请大佬们指教。
完整代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define rep(i,a,b) for(int i=a;i<b;i++)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
const int maxn=5005;
ll sum[maxn<<2]={0};
ll nums[maxn]={0};
void pushup(ll rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void updata(ll L,ll R,ll l,ll r,ll rt){
if(l==r)
{
sum[rt]++;
return;
}
ll m=(l+r)>>1;
if(m>=L) updata(L,R,l,m,rt<<1);
if(m<R) updata(L,R,m+1,r,rt<<1|1);
pushup(rt);
}
ll equry(ll L,ll R,ll l,ll r,ll rt){
if(L<=l&&R>=r){
return sum[rt];
}
pushup(rt);
ll m=(l+r)>>1;
ll ans=0;
if(m>=L) ans+=equry(L,R,lson);
if(m<R) ans+=equry(L,R,rson);
return ans;
}
int main()
{
int n;
ll counter=0;
while(~scanf("%d",&n))
{
counter=0;
memset(nums,0,sizeof(nums));
memset(sum,0,sizeof(sum));
rep(i,0,n){
scanf("%lld",&nums[i]);
updata(nums[i]+1,nums[i]+1,1,n+1,1);
counter+=equry(nums[i]+1+1,n+1,1,n+1,1);
}
// cout<<"ok"<<endl;
ll minn=counter;
rep(i,0,n){
counter+=(n-2*nums[i]-1);
minn=min(counter,minn);
}
cout<<minn<<endl;
}
return 0;
}