【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;
} 
原文地址:https://www.cnblogs.com/Invictus-Ocean/p/12995516.html