[题解]洛谷P1966 火柴排队

原题链接

对于两个队列a,b,求怎么每次只序列中交换相邻两个数使∑(a[i]-b[i])2 最小,输出次数对99,999,997取余

展开得:∑a[i]2+b[i]2 -∑2a[i]b[i]

而∑a[i]2+b[i]2 是不变的,所以只要最大化∑2a[i]b[i]即可。

首先可以确定,对两个序列的变化完全可以集中到一个序列里完成

一个贪心策略:将a中最大的数和b中最大的数配对,第二大的和第二大的……

然后把a中每一个数的标记设为他应该对应的b[i]的i,设这个标记序列为c

然后对c从小到大排序的过程中统计逆序对即可

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const int mod = 99999997,MAXN = 1000010;

int n,c[MAXN];

struct node{
    int n,p,tag;
}a[MAXN],b[MAXN];

int cmp1(node lxl,node chen_zhe){
    return lxl.n<chen_zhe.n;
}

int cmp2(node lxl,node chen_zhe){
    return lxl.p<chen_zhe.p;
}

int temp[MAXN];
long long ans=0;
void merge_sort(int l,int r){
    if(r-l>1){
        int m=(l+r)/2;
        merge_sort(l,m);
        merge_sort(m,r);
        int p1=l,p2=m,i=l;
        while(p1<m||p2<r){
            if(p2>=r||(p1<m&&c[p1]<=c[p2]))temp[i++]=c[p1++];
            else {
                temp[i++]=c[p2++];
                ans+=(m-p1)%mod;
            }
        }
        for(i=l;i<r;i++)c[i]=temp[i];
    }
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i].n);
        a[i].p=i;
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&b[i].n);
        b[i].p=i;
    }
    sort(a+1,a+n+1,cmp1);
    sort(b+1,b+n+1,cmp1);
    for(int i=1;i<=n;i++)
        a[i].tag=b[i].p;
    sort(a+1,a+n+1,cmp2);
    for(int i=1;i<=n;i++)
        c[i]=a[i].tag;
    merge_sort(1,n+1);
    printf("%lld",ans%mod);
    return 0;
}
本篇文章为SHINE_GEEK原创,转载请注明来源!
written_by:SHINE_GEEK

-------------------------------------
签名:自己选的路,跪着也要走完;理想的实现,需要不懈奋斗!
-------------------------------------
原文地址:https://www.cnblogs.com/sjrb/p/10371982.html