NOIp2013提高组 火柴排队————逆序对+贪心

题解:本题主要考查逆序对,树状数组。
简要题意:两列火柴之间的距离为: ∑ ( a i − b i ) 2 \sum(a_i-b_i)^2 (aibi)2,每列火柴中相邻两根火柴的位置都可以交换,请通过交换使两列火柴之间的距离最小,求最少交换次树数。
1.贪心:我们要 ∑ ( f i r i − s e c i ) 2 \sum(fir_i-sec_i)^2 (firiseci)2最小,相当于要最小化 ( f i r i − s e c i ) (fir_i−sec_i) (firiseci),也就是fir列第k大的元素必须和sec列中第k大的元素位置一样。
2.逆序对+归并排序:这道题目的最难点在于新建序列!按高度排列,得出他们的序号序列a,b。如样例2:
Fir:1 3 4 2->1 2 3 4 A:对应原编号为:1 4 2 3
Sec:1 7 2 4->1 2 4 7 B:对应原编号为:1 3 4 2
新的序列表示(Fir为例):第一小的数在原序列第一位,第二小的数在原序列第四位(第 k k k大的在原序列的位置)
所以要把元素按第k大的元素位置对应,就只用将序列A,B交换相等即可,问题就便为求逆序对数了。
代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
struct N
{
	long long ans,num;
}fir[222222],sec[222222];
long long n,ans;
long long a[222222],b[222222];
bool cmp(N x,N y)
{
	return x.ans<y.ans;
} 
void Bgui(int l,int r)
{
	if(l>=r)return ;
	int mid=l+r>>1;
	int i=l;int j=mid+1;int k=l;
	Bgui(l,mid);Bgui(mid+1,r);
	while(i<=mid&&j<=r)
    {
        if(a[i]>a[j])
        {
            b[k++]=a[j++];
            ans+=mid-i+1;
            ans%=99999997;
        }
        else b[k++]=a[i++];
    }
    while(i<=mid) b[k++]=a[i++];
    while(j<=r) b[k++]=a[j++];
    for(int i=l;i<=r;i++)a[i]=b[i];
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>fir[i].ans;
		fir[i].num=i;
	}
	for(int i=1;i<=n;i++)
	{
		cin>>sec[i].ans;
		sec[i].num=i;
	}
	sort(fir+1,fir+1+n,cmp);
	sort(sec+1,sec+1+n,cmp);
	for(int i=1;i<=n;i++)
	a[fir[i].num]=sec[i].num;
	Bgui(1,n);
	cout<<ans<<endl;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值