【NOIP2013】火柴排队 题解(贪心+归并排序)
前言:一道水题。
-----------------------
题目大意:给出数列$a_i$和$b_i$,问使$\sum_{i=1}^n (a_i-b_i)^2$最小的最少操作次数。
首先,如果两个数列相同位置的数排名相同,那么符合题意。现在我们证明一下:
证明:$a_i<a_j,b_i<b_j,(a_i-b_i)^2+(a_j-b_j)^2<(a_i-b_j)^2+(b_i-a_j)^2$
$(a_i-b_j)^2+(b_i-a_j)^2=a_i^2+b_i^2+a_j^2+b_j^2-2a_ib_j-2b_ia_j$
$(a_i-b_i)^2+(a_j-b_j)^2=a_i^2+b_i^2+a_j^2+b_j^2-2a_ib_i-2a_jb_j$
上式减下式得:$2a_i(b_i-b_j)+2a_j(b_j-b_i)$
$=2a_i(b_i-b_j)-2a_j(b_i-b_j)$
$=2(a_i-a_j)(b_i-b_j)>0$
所以$(a_i-b_i)^2+(a_j-b_j)^2<(a_i-b_j)^2+(b_i-a_j)^2$。
证毕。
计算次数的话就是比较新的位置和之前的位置,归并排序解决。其实就是归并排序求逆序对的变形。
代码:
#include<bits/stdc++.h> #define int long long using namespace std; const int mod=99999997; int n,c[1000005],r[1000005],ans; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){ if (ch=='-') f=-1; ch=getchar(); }while(isdigit(ch)){ x=x*10+ch-'0'; ch=getchar(); } return x*f; } struct node { int x,l; }a[1000005],b[1000005]; bool cmp(node s,node y) { return s.x<y.x; } void msort(int l,int ri) { if (l>=ri) return; int mid=(l+ri)>>1; msort(l,mid); msort(mid+1,ri); int i,j,k; for (i=l,j=mid+1,k=l;i<=mid&&j<=ri;) if (c[i]>c[j]) { ans=(ans+ri-j+1)%mod; r[k]=c[i];i++;k++; } else { r[k]=c[j];k++;j++; } for (;i<=mid;i++,k++) r[k]=c[i]; for (;j<=ri;j++,k++) r[k]=c[j]; for (int s=l;s<=ri;s++) c[s]=r[s]; } signed main() { n=read(); for (int i=1;i<=n;i++) a[i].x=read(),a[i].l=i; for (int i=1;i<=n;i++) b[i].x=read(),b[i].l=i; sort(a+1,a+n+1,cmp); sort(b+1,b+n+1,cmp); for (int i=1;i<=n;i++) c[b[i].l]=a[i].l; msort(1,n); cout<<ans; return 0; }