洛谷 P1966 火柴排队
先研究第一个问题:如何使得"距离"最小。
可以发现题意就是要求一种合适的两组数间的配对方式,使得∑(ai-bi)^2最小。
我口胡了一个结论...就是最好的配对方式,就是两组数分别排好序后,将位置相同的两个元素配对。
当然,可以发现这个结论是对的:https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P1966
(以下内容仅为记录)
题目的意思:min{∑(ai-bi)^2 (1<=i<=n)}
展开:min{∑(ai^2+bi^2-2*ai*bi)}=min{∑ai^2+∑bi^2-∑2*ai*bi}
仔细观察,可以发现∑ai^2和∑bi^2的值是不会变的,所以只能在∑2*ai*bi上做文章。
为使得和最小,那么∑2*ai*bi要最大,本题的模型就转变为max{∑ai*bi}。(2为常数,可省略)
对两个数组分别进行排序后,现有第一个数组中两个数a和b,第二个数组中两个数c和d
a<=b c<=d
根据以上猜测,则ac+bd一定是最大的。利用反证法。
若ac+bd不是最大的,那么一定有比它更大的,只有ad+bc
如果c=d,那么显然ac+bd=ad+bc,不会更大
如果c<d,那么
ac+bd<ad+bc
ac-ad<bc-bd
a*(c-d)<b*(c-d)//c-d<0
a>b,与a<=b矛盾
再研究下一个问题:如何得到最小的距离。
首先读入两个数组a1,b。将a1放入一个结构体数组a,结构体有成员a,b,num,a表示a1数组中对应位置的值,num表示最初的位置,b空着。
此时得到x1:就是开始时的b数组。
然后将a以结构体的成员a为关键字排序,将b排序(都是从小到大)。再将b中元素按此时的顺序赋给a的成员(意会一下)。这样就能得到最好的配对方式(两组数分别排好序后,将位置相同的两个元素配对)。
再将a以结构体成员num为关键字排序。此时得到x2:就是将a中结构体成员b按顺序抽出组成的新数组。
那么,问题就转化为在只能进行交换相邻两数的操作时,将x1用最少步数变成x2。
这里的方法是:将x1中每个数字变为其在x2中最后一次出现的位置。问题就变成将新的x1变成从小到大的最少步数。
这个问题的答案,其实就是新的x1的逆序对个数。因为:每一次交换相邻两数,不会改变其他数和这两个数的相对位置,因此只会导致逆序对个数加1、减1或不变。那么,可以设法使得每一次交换都恰好交换逆序的两数(很显然是可以做到的)。这儿有个解释:http://blog.csdn.net/sunmenggmail/article/details/8151793
1 #include<cstdio> 2 #include<algorithm> 3 #include<map> 4 #define md 99999997 5 using namespace std; 6 typedef long long LL; 7 struct X 8 { 9 LL a,b,num; 10 friend bool operator<(const X& a,const X& b) 11 { 12 return a.a<b.a; 13 } 14 }a[100100]; 15 map<LL,LL> ma; 16 LL now,n; 17 LL x1[100100],ans,t1[100100]; 18 bool cmp(const X& a,const X& b) 19 { 20 return a.num<b.num; 21 } 22 void sort2(LL l,LL r) 23 { 24 if(l==r) return; 25 LL m=(l+r)>>1,i,j,k; 26 sort2(l,m); 27 sort2(m+1,r); 28 for(i=k=l,j=m+1;i<=m&&j<=r;) 29 { 30 if(x1[i]>x1[j]) 31 { 32 ans=(ans+m+1-i+md)%md; 33 t1[k++]=x1[j++]; 34 } 35 else 36 t1[k++]=x1[i++]; 37 } 38 for(;i<=m;) 39 t1[k++]=x1[i++]; 40 for(;j<=r;) 41 t1[k++]=x1[j++]; 42 for(i=l;i<=r;i++) 43 x1[i]=t1[i]; 44 } 45 int main() 46 { 47 LL i; 48 //freopen("testdata.in","r",stdin); 49 scanf("%lld",&n); 50 for(i=1;i<=n;i++) 51 scanf("%lld",&a[i].a); 52 for(i=1;i<=n;i++) 53 { 54 scanf("%lld",&a[i].b); 55 x1[i]=t1[i]=a[i].b; 56 a[i].num=i; 57 } 58 sort(a+1,a+n+1); 59 sort(t1+1,t1+n+1); 60 for(i=1;i<=n;i++) 61 a[i].b=t1[i]; 62 sort(a+1,a+n+1,cmp); 63 for(i=1;i<=n;i++) 64 ma[a[i].b]=++now; 65 for(i=1;i<=n;i++) 66 x1[i]=ma[x1[i]]; 67 sort2(1,n); 68 printf("%lld",ans); 69 return 0; 70 }
错误代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define md 99999997 5 using namespace std; 6 typedef long long LL; 7 struct X 8 { 9 LL a,b; 10 friend bool operator<(const X& a,const X& b) 11 { 12 return a.a<b.a; 13 } 14 }a[100100]; 15 LL a1[100100],t1[100100]; 16 LL n,ans; 17 void sort2(LL l,LL r) 18 { 19 if(l==r) return; 20 LL m=(l+r)>>1,i,j,k; 21 sort2(l,m); 22 sort2(m+1,r); 23 for(i=k=l,j=m+1;i<=m&&j<=r;) 24 { 25 if(a1[i]>a1[j]) 26 { 27 ans=(ans+m+1-i+md)%md; 28 t1[k++]=a1[j++]; 29 } 30 else 31 t1[k++]=a1[i++]; 32 } 33 for(;i<=m;) 34 t1[k++]=a1[i++]; 35 for(;j<=r;) 36 t1[k++]=a1[j++]; 37 for(i=l;i<=r;i++) 38 a1[i]=t1[i]; 39 } 40 int main() 41 { 42 LL i; 43 //freopen("testdata.in","r",stdin); 44 scanf("%lld",&n); 45 for(i=1;i<=n;i++) 46 scanf("%lld",&a[i].a); 47 for(i=1;i<=n;i++) 48 scanf("%lld",&a[i].b); 49 sort(a+1,a+n+1); 50 for(i=1;i<=n;i++) 51 a1[i]=a[i].b; 52 sort2(1,n); 53 printf("%lld\n",ans); 54 return 0; 55 }
还是na1ve。