[NOIP2013提高&洛谷P1966]火柴排队 题解(树状数组求逆序对)

[NOIP2013提高&洛谷P1966]火柴排队

Description

涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为: ∑(ai-bi)^2
其中 ai 表示第一列火柴中第 i 个火柴的高度,bi 表示第二列火柴中第 i 个火柴的高度。
每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模的结果。

输入格式:
共三行,第一行包含一个整数 n,表示每盒中火柴的数目。
第二行有 n 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。
第三行有 n 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。

输出格式:
输出共一行,包含一个整数,表示最少交换次数对 99,999,997 取模的结果。

Solution

1.要是两列火柴距离最小,那么我们就应让所有的(ai-bi)^2最小,即ai与bi的差最小,考虑贪心,应该让在序列中从小到大排序后序号相同的数相减,因为如果两数列中两个数在各自数列中从小到大序号相等,那么如果这两个数不做差分别与另一个数做差,差的平方显然是要比原来大的。

2.那么我们考虑如何将数列整理成想要的:我们建立一个新的数列,以离散后的第一个数列该项为下标,以离散后第二个数列对应的项为值建立,相当于以第一个数列为关键字排序第二个数列,然后跑一遍逆序对即可。

树状数组求逆序对的方法参考我的博文:http://www.cnblogs.com/COLIN-LIGHTNING/p/8621294.html

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;

int now[100100],c[100100],n,m,i,j,k;
struct nums{
	int num,d;
}a1[100100],a2[100100];

inline int rd(){
    int x=0;
    char c=getchar();
	bool f=false;
	while(!isdigit(c)){ 
		if(c=='-') f=true;
		c=getchar();
	}
	while(isdigit(c)){
		x=(x<<1)+(x<<3)+(c^48);
		c=getchar();
	}
	return f?-x:x;
}

inline int lowbit(int x){return x&-x;}

void add(int x){
	while(x<=n){
		c[x]+=1;
		x+=lowbit(x);
	}
}

int sum(int x){
	int ret=0;
	while(x>0){
		ret+=c[x];
		x-=lowbit(x);
	}
	return ret;
}

bool cmp(nums x,nums y){
	return x.num<y.num;
}
int main(){
	memset(c,0,sizeof(c));
	n=rd();
	for(i=1;i<=n;++i){
		a1[i].num=rd();
		a1[i].d=i;
	}
	for(i=1;i<=n;++i){
		a2[i].num=rd();
		a2[i].d=i;
	}
	sort(a1+1,a1+1+n,cmp);
	sort(a2+1,a2+1+n,cmp);
	for(i=1;i<=n;++i) now[a1[i].d]=a2[i].d;
	long long ans=0;
	for(i=n;i>0;--i){
		add(now[i]);
		ans=(ans+sum(now[i]-1))%99999997;
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2018-03-22 10:14  COLINGAO  阅读(468)  评论(0编辑  收藏  举报