Label
一个经典贪心问题(排序不等式)与一个经典逆序对问题(乱序序列有序的最小相邻位交换次数)的组合
Description
给定两个长度为 n ( 1 ≤ n ≤ 1 0 5 ) n(1\leq n \leq10^5) n(1≤n≤105)的数列 a , b ( 0 ≤ a i , b i ≤ 2 31 a,b(0\leq a_i,b_i\leq 2^{31} a,b(0≤ai,bi≤231且同一列火柴的高度互不相同 ) ) ),现规定可对 a , b a,b a,b执行任意此相邻位元素交换数值的操作,求最小的操作次数,使得 ∑ i = 1 n ( a i − b i ) 2 \sum_{i=1}^{n}(a_i-b_i)^2 ∑i=1n(ai−bi)2最小化。答案对 1 0 8 − 3 10^8-3 108−3取模。
Solution
考虑如何最小化 ∑ i = 1 n ( a i − b i ) 2 \sum_{i=1}^{n}(a_i-b_i)^2 ∑i=1n(ai−bi)2。
由于
∑
i
=
1
n
(
a
i
−
b
i
)
2
=
∑
i
=
1
n
(
a
i
2
+
b
i
2
−
2
a
i
b
i
)
\sum_{i=1}^n(a_i-b_i)^2=\sum_{i=1}^n(a_i^2+b_i^2-2a_ib_i)
i=1∑n(ai−bi)2=i=1∑n(ai2+bi2−2aibi)
∑ i = 1 n ( a i 2 + b i 2 ) \sum_{i=1}^n(a_i^2+b_i^2) ∑i=1n(ai2+bi2)不受 a 、 b a、b a、b元素排列顺序影响,故本题等价于令 ∑ i = 1 n a i b i \sum_{i=1}^{n}a_ib_i ∑i=1naibi最大化。这个问题其实是就是排序不等式解决的问题,有如下结论:顺序之乘之和 ≥ \geq ≥乱序乘法之和 ≥ \geq ≥反序乘法之和。
证明此处排序不等式的证明方法为典型的微扰:
现有四个正数 a 1 , a 2 , b 1 , b 2 , a 1 < a 2 , b 1 < b 2 a_1,a_2,b_1,b_2,a_1<a_2,b_1<b_2 a1,a2,b1,b2,a1<a2,b1<b2,则必有 a 1 b 1 + a 2 b 2 > a 1 b 2 + a 2 b 1 a_1b_1+a_2b_2>a_1b_2+a_2b_1 a1b1+a2b2>a1b2+a2b1。
证明:对上述不等式移项,发现其等价于 a 2 ( b 2 − b 1 ) > a 1 ( b 2 − b 1 ) a_2(b_2-b_1)>a_1(b_2-b_1) a2(b2−b1)>a1(b2−b1),由于 b 2 − b 1 > 0 且 a 2 > a 1 b_2-b_1>0且a_2>a_1 b2−b1>0且a2>a1,故该不等式显然成立,命题得证。
由于任意乱序数列可经多次顺序交换变为顺序数列,故此处微扰可推广到一般情况,成立。
对于此题,满足条件的 a a a与 b b b一定满足的条件是: ∀ i ∈ [ 1 , n ] , r a n k a i = r a n k b i \forall i\in[1,n],rank_{a_i}=rank_{b_i} ∀i∈[1,n],rankai=rankbi,其中 r a n k rank rank表示 a 、 b a、b a、b任意元素在各自序列内的值排名(默认最小数为1)。由于某个两列同时移动火柴的方案等价于只移动一列内火柴的方案,故我们不妨考虑如何移动 a a a内元素使其满足 r a n k a i = r a n k b i rank_{a_i}=rank_{b_i} rankai=rankbi。我们先对两个数列进行离散化,即令 a i = r a n k a i , b i = r a n k b i a_i=rank_{a_i},b_i=rank_{b_i} ai=rankai,bi=rankbi,之后,我们当前的目标是令 a a a序列与 b b b相等,故构造一个映射 a [ p l a a [ b [ i ] ] ] = i a[plaa[b[i]]]=i a[plaa[b[i]]]=i(值排名为i的数在对应序列的位置为 p l a a [ i ] / p l a b [ i ] plaa[i]/plab[i] plaa[i]/plab[i] ),我们令 a a a与 b b b相等便等价于使 a [ i ] = i a[i]=i a[i]=i恒成立,即将乱序的 a a a通过邻位交换变为升序数列。问题就变为,将原本乱的 a a a序列升序排列的最少交换次数。
这是一个经典问题,答案是 a a a内逆序对的个数。
证明:
1、存在一个方案使得交换次数等于 a a a内逆序对的个数:
交换方法:等价于冒泡排序—先将序列内最大的数交换到末位,然后将序列内次大的数交换到倒数第二位,依此类推。显然,每一个数(设其值为 k k k)被交换的次数等于从其在原数列位置往后小于它的元素的个数,即所有逆序对 ( i , j ) ( i > j ) (i,j)(i>j) (i,j)(i>j)中, i = k i=k i=k的逆序对的个数,那么整个方案的交换次数便是整个序列内的逆序对个数了。
2、任意交换方案次数一定大于等于 a a a内逆序对的个数:此处考虑每一个数的位置交换即可。
综上,将原本乱的 a a a序列升序排列的最少交换次数是 a a a内逆序对的个数。命题得证
最后,需要注意的是:此题规定每列内火柴高度互不相同,这样一来省去了一些关于离散化与求逆序对个数时细节处理上的麻烦。
Code
#include<cstdio>
#include<iostream>
#include<algorithm>
#define ri register int
using namespace std;
const int MAXN=1e5+20,MOD=1e8-3;
int n,a[MAXN],plaa[MAXN],b[MAXN],plab[MAXN],tmp[MAXN],ans;
struct node{
int val,num;
}s[MAXN];
bool cmp(const node &x,const node &y){
return x.val < y.val;
}
void mergesort(int l,int r)//归并排序
{
if(l==r) return;
ri mid=(l+r)>>1;
mergesort(l,mid); mergesort(mid+1,r);
for(ri i=l;i<=r;++i) tmp[i]=a[i];
ri head=l,tail=mid+1,cnt=l;
while(head<=mid&&tail<=r)
{
if(tmp[head]>tmp[tail])
a[cnt++]=tmp[tail++];
if(tail>r) break;
if(tmp[head]<=tmp[tail])
{
ans=(ans+tail-mid-1)%MOD;
a[cnt++]=tmp[head++];
}
}
if(head<=mid)
for(ri i=head;i<=mid;++i)
{
a[cnt++]=tmp[i];
ans=(ans+r-mid)%MOD;
}
if(tail<=r)
for(ri i=tail;i<=r;++i) a[cnt++]=tmp[i];
}
void init(int x[],int pla[])
{
for(ri i=1;i<=n;++i)
{
scanf("%d",&s[i].val);
s[i].num=i;
}
sort(s+1,s+n+1,cmp);
for(ri i=1;i<=n;++i)
{
pla[i]=s[i].num;
x[pla[i]]=i;
}
}
int main()
{
scanf("%d",&n);
init(a,plaa); init(b,plab);
for(ri i=1;i<=n;++i)
a[plaa[b[i]]]=i;
mergesort(1,n);
cout<<ans;
return 0;
}