HDU - 1394 Minimum Inversion Number 解题报告(线段树)(数学)

题目大意

链接: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;
 } 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值