[NOIP2013TG] 洛谷 P1966 火柴排队
题意
给定2个等长序列a、b,要求通过交换使
\[\sum_{i=1}^{n}(a_i-b_i)^2
\]
最小。
分析
看着这个式子,我突然想到了方差。很明显,方差反应数据的波动程度,所以让数据集中就可以使方差变小了。而对应到这个公式,大方向就是让这两个数据尽可能接近。很容易想到分别排序,\(a_i,b_i\)就是对应数了(自己取的名字)。
这怎么证明呢?因为这个公式中带有平方,这会增加极限值对结果的贡献,所以要尽可能缩小最大差值(这也是方差教给我们的w)
那么要让a、b两个序列中的数对应,就要将a与b中的数建立一一对应的映射关系。这样,其中一个序列会作为排序的判定条件(也可以理解为判定大小的标准),而另一个序列就是要交换的序列了。这一过程其实就是离散化。
排序与求逆序对复杂度都是\(O(nlog_2n)\),10w可过。
代码
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=100005,mod=99999997;
int n,m;
int p[MAXN],arr[MAXN],ans;
struct match{
int value,order;
}a[MAXN],b[MAXN];
bool comp(match m1,match m2)
{
return m1.value<m2.value;
}
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int num)
{
for(int i=x;i<=n;i+=lowbit(i))
arr[i]+=num;
return;
}
int sum(int x)
{
int temp=0;
for(int i=x;i;i-=lowbit(i))
temp+=arr[i];
return temp;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i].value,a[i].order=i;
for(int i=1;i<=n;i++)
cin>>b[i].value,b[i].order=i;
//order记录顺序
sort(a+1,a+1+n,comp);
sort(b+1,b+1+n,comp);
//排序后就将两个序列建立对应关系了
for(int i=1;i<=n;i++)
p[b[i].order]=a[i].order;
for(int i=1;i<=n;i++)
add(p[i],1),ans=(ans+i-sum(p[i]))%mod;
//树状数组求逆序对
cout<<ans;
return 0;
}