P1966 火柴排队
Posted lpf-666
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1966 火柴排队相关的知识,希望对你有一定的参考价值。
P1966 火柴排队
求逆序对+数学
题意描述
有 a,b 两个数列,它们的距离为∑(ai-bi)2。
每列中相邻的两个数均可进行交换,求至少交换多少次,可以使 a,b 的距离最短。
看不懂你这题就别做了。
算法分析
首先你应该知道:顺序和≥乱序和≥逆序和(好像没有什么关系)
然后你应该知道它的证明方法,然后你就可以证出来这道题了。
但在证明之前,我们先感性地思考一下:a,b 数列中最大的相减,次大的相减…,次小的相减,最小的相减距离最小。
证明:A1>A2,B1>B2那么(A1-B1)2+(A2-B2)2<(A1-B2)2+(A2-B1)2。
假设(A1-B1)2+(A2-B2)2>(A1-B2)2+(A2-B1)2
A1^2 -2*A1*B1+B1^2+A2^2 -2*A2*B2+B2^2>A1^ 2-2*A1*B2+B2^2+ A2^ 2-2*A2*B1+B1^2
? 2*A1*B1+2*A2*B2<2*A1*B2+2*A2*B1
? A1*B1+A2*B2<A1*B2+A2*B1
? A1(B1-B2)<A2(B1-B2)
? A1<A2
得出矛盾,所以假设不成立。
证毕。
好了,这道题目的精华在于对于新建序列!
假设我们现在有离散化后的序列 a={4,3,1,2},b={1,3,2,4}。
我们令 q[a[i]]=b[i],相当于以 a[i] 为关键字对序列 b[i] 排序。
若序列 a 与序列 b 相等,那么此时 q[a[i]] 应该等于 a[i] 的,也就是 q[i]=i。
那么也就是说如果我们想让序列 a 与序列 b 相等,那么我们需要让 q 升序排列。
问题就变为,将原本乱的 q 序列升序排列的最少交换次数。
诶,这不就是逆序对吗?
所以用归并排序求解即可。
代码实现
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#define maxn 100010
#define mod 99999997
using namespace std;
int n,ans=0;
int c[maxn],q[maxn];
struct node{
int num;//序号
int big;//数据
}a[maxn],b[maxn];
bool cmp(node x,node y){
return x.big<y.big;
}
void init(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i].big);
a[i].num=i;
}
for(int i=1;i<=n;i++){
scanf("%d",&b[i].big);
b[i].num=i;
}
sort(a+1,a+n+1,cmp);
sort(b+1,b+n+1,cmp);
for(int i=1;i<=n;i++) q[a[i].num]=b[i].num;
return;
}
void work(int l,int r){
if(l==r) return;
int mid=(l+r)>>1;
work(l,mid);work(mid+1,r);
int i=l,j=mid+1,k=l;
while(i<=mid&&j<=r){
if(q[i]<=q[j]) c[k++]=q[i++];
else{
c[k++]=q[j++];
ans+=(mid-i+1)%mod;
ans%=mod;
}
}
while(i<=mid) c[k++]=q[i++];
while(j<=r) c[k++]=q[j++];
for(int p=l;p<=r;p++) q[p]=c[p];
}
int main(){
init();
memset(c,0,sizeof(c));
work(1,n);
printf("%d
",ans%mod);
//system("pause");
return 0;
}
结语
OI靠数学。
以上是关于P1966 火柴排队的主要内容,如果未能解决你的问题,请参考以下文章