Label
一个经典贪心问题(排序不等式)与一个经典逆序对问题(乱序序列有序的最小相邻位交换次数)的组合
Description
给定两个长度为n(1≤n≤105)n(1\leq n \leq10^5)n(1≤n≤105)的数列a,b(0≤ai,bi≤231a,b(0\leq a_i,b_i\leq 2^{31}a,b(0≤ai,bi≤231且同一列火柴的高度互不相同))),现规定可对a,ba,ba,b执行任意此相邻位元素交换数值的操作,求最小的操作次数,使得∑i=1n(ai−bi)2\sum_{i=1}^{n}(a_i-b_i)^2∑i=1n(ai−bi)2最小化。答案对108−310^8-3108−3取模。
Solution
考虑如何最小化∑i=1n(ai−bi)2\sum_{i=1}^{n}(a_i-b_i)^2∑i=1n(ai−bi)2。
由于
∑i=1n(ai−bi)2=∑i=1n(ai2+bi2−2aibi)\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=1n(ai2+bi2)\sum_{i=1}^n(a_i^2+b_i^2)∑i=1n(ai2+bi2)不受a、ba、ba、b元素排列顺序影响,故本题等价于令∑i=1naibi\sum_{i=1}^{n}a_ib_i∑i=1naibi最大化。这个问题其实是就是排序不等式解决的问题,有如下结论:顺序之乘之和≥\geq≥乱序乘法之和≥\geq≥反序乘法之和。
证明此处排序不等式的证明方法为典型的微扰:
现有四个正数a1,a2,b1,b2,a1<a2,b1<b2a_1,a_2,b_1,b_2,a_1<a_2,b_1<b_2a1,a2,b1,b2,a1<a2,b1<b2,则必有a1b1+a2b2>a1b2+a2b1a_1b_1+a_2b_2>a_1b_2+a_2b_1a1b1+a2b2>a1b2+a2b1。
证明:对上述不等式移项,发现其等价于a2(b2−b1)>a1(b2−b1)a_2(b_2-b_1)>a_1(b_2-b_1)a2(b2−b1)>a1(b2−b1),由于b2−b1>0且a2>a1b_2-b_1>0且a_2>a_1b2−b1>0且a2>a1,故该不等式显然成立,命题得证。
由于任意乱序数列可经多次顺序交换变为顺序数列,故此处微扰可推广到一般情况,成立。
对于此题,满足条件的aaa与bbb一定满足的条件是:∀i∈[1,n],rankai=rankbi\forall i\in[1,n],rank_{a_i}=rank_{b_i}∀i∈[1,n],rankai=rankbi,其中rankrankrank表示a、ba、ba、b任意元素在各自序列内的值排名(默认最小数为1)。由于某个两列同时移动火柴的方案等价于只移动一列内火柴的方案,故我们不妨考虑如何移动aaa内元素使其满足rankai=rankbirank_{a_i}=rank_{b_i}rankai=rankbi。我们先对两个数列进行离散化,即令ai=rankai,bi=rankbia_i=rank_{a_i},b_i=rank_{b_i}ai=rankai,bi=rankbi,之后,我们当前的目标是令aaa序列与bbb相等,故构造一个映射a[plaa[b[i]]]=ia[plaa[b[i]]]=ia[plaa[b[i]]]=i(值排名为i的数在对应序列的位置为plaa[i]/plab[i]plaa[i]/plab[i]plaa[i]/plab[i] ),我们令aaa与bbb相等便等价于使a[i]=ia[i]=ia[i]=i恒成立,即将乱序的aaa通过邻位交换变为升序数列。问题就变为,将原本乱的aaa序列升序排列的最少交换次数。
这是一个经典问题,答案是aaa内逆序对的个数。
证明:
1、存在一个方案使得交换次数等于aaa内逆序对的个数:
交换方法:等价于冒泡排序—先将序列内最大的数交换到末位,然后将序列内次大的数交换到倒数第二位,依此类推。显然,每一个数(设其值为kkk)被交换的次数等于从其在原数列位置往后小于它的元素的个数,即所有逆序对(i,j)(i>j)(i,j)(i>j)(i,j)(i>j)中,i=ki=ki=k的逆序对的个数,那么整个方案的交换次数便是整个序列内的逆序对个数了。
2、任意交换方案次数一定大于等于aaa内逆序对的个数:此处考虑每一个数的位置交换即可。
综上,将原本乱的aaa序列升序排列的最少交换次数是aaa内逆序对的个数。命题得证
最后,需要注意的是:此题规定每列内火柴高度互不相同,这样一来省去了一些关于离散化与求逆序对个数时细节处理上的麻烦。
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;
}
680

被折叠的 条评论
为什么被折叠?



