P1966 火柴排队【逆序对】

洛谷P1966

题目大意

给两个序列 a i , b i a_i,b_i ai,bi ,可以交换相邻两数,要求满足 i = 1 n ( a i b i ) 2 \sum_{i=1}^{n} (a_i-b_i)^2 i=1n(aibi)2 最小 ,求最少的交换次数

题目分析

i = 1 n ( a i b i ) 2 \sum_{i=1}^{n} (a_i-b_i)^2 i=1n(aibi)2 最小
只需满足,大的和大的配对,小的和小的配对
找到 b i b_i bi序列中的数对应 a i a_i ai序列中的数的位置。
则求出了最终的变换位置。由于只能相邻交换
对位置求逆序对即可

代码详解

#include <bits/stdc++.h>
#define fi first
#define se second
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
typedef pair<ll,int> PLI;
int n;
const int maxn = 1e6+50;
PLI a[maxn],b[maxn];
ll c[maxn];
ll tmp[maxn];
ll ans=0;
const int mod = 99999997;
void merge(int l1,int r1,int l2,int r2)
{
    int i=l1;
    int j=l2;
    int id=l1;
    while(i<=r1&&j<=r2){
        if(c[i]<=c[j]) {
            tmp[id++] = c[i];
            i++;
        }
        else {
            ans =(ans+ (r1-i+1))%mod; 
            tmp[id++] = c[j];
            j++;
        }
    }
    while(i<=r1)  tmp[id++]=c[i++];
    while(j<=r2)  tmp[id++]=c[j++];
    for(int i=l1;i<=r2;i++)  c[i]=tmp[i];
}
void mergesort(int l,int r)
{
    if(l<r)
    {
        int mid = (r+l)/2;
        mergesort(l,mid);
        mergesort(mid+1,r);
        merge(l,mid,mid+1,r);
    }
}
int main()
{
    scanf("%d",&n);
    //对两行数据排序
    rep(i,1,n) {scanf("%lld",&a[i].fi);a[i].se=i;}
    rep(i,1,n) {scanf("%lld",&b[i].fi);b[i].se=i;}
    sort(a+1,a+n+1);
    sort(b+1,b+n+1);
    //找到第二行元素所匹配的第一行元素的位置
    rep(i,1,n){
        int t = b[i].se;
        c[t] = a[i].se;
    }
    //对c[i]数组求逆序对
    mergesort(1,n);
    printf("%lld\n",ans);
    return 0;
} 

/* 4 2 3 1 4 3 2 1 4 */
全部评论

相关推荐

手撕没做出来是不是一定挂
Chrispp3:不会,写出来也不一定过
点赞 评论 收藏
分享
M_bao:换个排版吧哥们,看着费劲
点赞 评论 收藏
分享
10-09 22:05
666 C++
找到工作就狠狠玩CSGO:报联合国演讲,报电子烟设计与制造
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务