NOIP2013 火柴排队

本文介绍了一种通过交换火柴位置来最小化两列火柴间距离的算法问题。利用排序不等式的原理,实现了一个高效算法,通过离散化处理和归并排序求解逆序对数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

涵涵有两盒火柴,每盒装有 $n$ 根火柴,每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为: $ \sum (a_i-b_i)^2$

其中 $ a_i$ 表示第一列火柴中第 $ i $ 个火柴的高度, $b_i$ 表示第二列火柴中第 $i$ 个火柴的高度。

每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 $99,999,997 $ 取模的结果。

输入输出格式

输入格式:

共三行,第一行包含一个整数 $ n$ ,表示每盒中火柴的数目。

第二行有 $ n $ 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。

第三行有 $n$ 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。

输出格式:

一个整数,表示最少交换次数对 $99,999,997$ 取模的结果。

输入输出样例

输入样例#1: 复制
4
2 3 1 4
3 2 1 4
输出样例#1: 复制
1
输入样例#2: 复制
4
1 3 4 2
1 7 2 4
输出样例#2: 复制
2

说明

【输入输出样例说明1】

最小距离是 $ 0$ ,最少需要交换 $1$ 次,比如:交换第 $1 $ 列的前 $ 2$ 根火柴或者交换第 $2$ 列的前 $2 $ 根火柴。

【输入输出样例说明2】

最小距离是 $10$ ,最少需要交换 $ 2 $ 次,比如:交换第 $ 1$ 列的中间 $ 2 $ 根火柴的位置,再交换第 $ 2$ 列中后 $2$ 根火柴的位置。

【数据范围】

对于 $10\%$ 的数据, $1 ≤ n ≤ 10$ ;

对于 $30\%$ 的数据, $1 ≤ n ≤ 100$ ;

对于 $60\%$ 的数据, $1 ≤ n ≤ 1,000$ ;

对于 $100\%$ 的数据, $1 ≤ n ≤ 100,000,0 ≤$ 火柴高度 $≤ maxlongint$


首先要知道一个叫做排序不等式的东西,总的来说就是反序和≤乱序和≤逆序和
详细的表述如下:
给出三个数列
a1,a2,...,ana_1,a_2,...,a_na1,a2,...,an
b1,b2,...,bnb_1,b_2,...,b_nb1,b2,...,bn
c1,c2,...,cnc_1,c_2,...,c_nc1,c2,...,cn
且满足
a1<a2<...<ana_1<a_2<...<a_na1<a2<...<an
b1<b2<...<bnb_1<b_2<...<b_nb1<b2<...<bn
数列c1,c2,...,cnc_1,c_2,...,c_nc1,c2,...,cn是随机排列后的数列b
那么有
a1bn+a2bn−1+...+anb1≤a1c1+a2c2+...+ancn≤a1b1+a2b2+...+ancna_1b_n+a_2b_{n-1}+...+a_nb_1≤a_1c_1+a_2c_2+...+a_nc_n≤a_1b_1+a_2b_2+...+a_nc_na1bn+a2bn1+...+anb1a1c1+a2c2+...+ancna1b1+a2b2+...+ancn

证明百度
题中式子$ \sum (a_i-b_i)2=\sum(a_i2-2a_ib_i+b_i^2)$
显然∑(ai2+bi2)\sum(a_i^2+b_i^2)(ai2+bi2)是常数,那么我们要“距离”最小,只需要让∑(aibi)\sum(a_ib_i)(aibi)最大
由排序不等式可得,要让aia_iaibib_ibi有序时,是最大的
但是我们并不需要让他们两个绝对的有序,只需要相对有序
让两队火柴中的一一对应即可
所以就需要离散化一下,开一个a_hash和b_hash,拷贝a和b中的元素,排序,然后一一把a,b中的元素对应成它们序号
然后再把b中的顺序当成排好序的,把a中的再对应一下之后再去寻找逆序对
开了这些数组之后空间算出来大概38M,可过
全部时间复杂度O(5nlog2n+n)O(5nlog_2n+n)O(5nlog2n+n)
2个nlogn是排序,2个是离散化,一个n是把b当成有序的之后转换a,一个nlogn是最后归并排序求逆序对
当然也可以用树状数组/线段树求逆序对,不过归并排序代码难度还是比较小的,所以就用了归并排序
有空再打一下树状数组和线段树的吧(咕咕咕)


代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define For(i,l,r) for(int i=l;i<=r;++i)
#define MAXN 1000010
#define M 99999997
#define mid ((l+r)>>1)
using namespace std;
long long read()
{
	char c;
	bool t=0;
	long long a=0;
	while((c=getchar())==' '||c=='\n'||c=='r');
	if(c=='-')
	{
		t=1;
		c=getchar();
	}
	while(isdigit(c))
	{
		a*=10;
		a+=(c-'0');
		c=getchar();
	}
	return a*(t?-1:1);
}
int n,mmap[MAXN],temp[MAXN];
long long a[MAXN],b[MAXN],a_hash[MAXN],b_hash[MAXN],ans;
int finda(long long x)
{
	return lower_bound(a_hash+1,a_hash+n+1,x)-a_hash;
}
int findb(long long x)
{
	return lower_bound(b_hash+1,b_hash+n+1,x)-b_hash;
}
void merge(int l,int r)
{
	int l1=l,l2=mid+1,r1=mid,r2=r,tl=l;
	while(l1<=r1&&l2<=r2)
	{
		if(a[l1]<a[l2])
		{
			temp[tl]=a[l1];
			++tl;
			++l1;
		}
		else//逆序对 
		{
			ans+=((r1-l1+1)%M);
			ans%=M;
			temp[tl]=a[l2];
			++tl;
			++l2;
		}
	}
	while(l1<=r1)
	{
		temp[tl]=a[l1];
		tl++;l1++;
	}
	while(l2<=r2)
	{
		temp[tl]=a[l2];
		tl++;l2++;
	}
	while(l<=r)
	{
		a[l]=temp[l];
		++l;
	}
}
void msort(int l,int r)
{
	if(r-l>1)
	{
		msort(l,mid);
		msort(mid+1,r);
	}
	merge(l,r);
}
int main()
{
	n=read();
	For(i,1,n)
	 a[i]=read();
	For(i,1,n)
	 b[i]=read();
	memcpy(a_hash,a,sizeof a);memcpy(b_hash,b,sizeof b);
	sort(a_hash+1,a_hash+n+1);sort(b_hash+1,b_hash+n+1);
	For(i,1,n)
	{
		a[i]=finda(a[i]);
		b[i]=findb(b[i]);
		mmap[b[i]]=i;
	}
	For(i,1,n)
	 a[i]=mmap[a[i]];
	msort(1,n);
	printf("%lld",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值