【noip 2013】火柴排隊

去題面的傳送門
QAQ昨天隊內胡策的T1,成功打次了
沒做過火柴排隊的我考完試先跑過來做這道題了
首先,對於兩個序列,要使他們之間的距離最小,也就是Σ(ai+bi)^2最小,肯定是最大的和最大的對應,次大的和次大的對應,也就是說,把兩個序列排序後,各個位上一一對應。但是還要保證交換次數最少,所以不能打亂順序。
舉個栗子:
A序列:4 7 2 1
B序列:3 2 1 4
排序後:
A:1 2 4 7
B:1 2 3 4
所以我們得到1,1對應,2,2對應,4,3對應,7,4對應,所得距離最小
對於原序列,我們把每個數字排序後的序號標記在對應的原位置上
也就是:
A:3 4 2 1
B:3 2 1 4
現在問題轉化爲,如何交換B序列中的相鄰數字,使用最少的步數來使B變爲A
現在,我用C數組表示,如果要使B交換後和A相同,各個數字應該放在哪個位置上
C:1 3 4 2
(當然,C數組的統計,還要用一個類似於模擬map函數的數組實現)
也就是說交換完成後的B序列,對應的C應該是1 2 3 4的嚴格遞增1的序列
所以我們要用最少的交換次數,來使C變爲一個嚴格上升的序列,如何求最少交換次數?
求逆序對個數
證明:
對於一個嚴格遞增的序列,一定不存在逆序對。對於一個非嚴格遞增的序列,一定存在相鄰的數字爲一對逆序對,所以,我們的最小交換次數就是逆序對的個數。
我用的歸併排序,之前有歸併排序求逆序對的博客

PS:數組模擬map求C數組的時候有點暈,一定要仔細想想

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn=100000+10,mo=99999997;
int n,ans;
int c[maxn],fin[maxn],m[maxn];
struct hh
{
    int num,x;
}a[maxn],b[maxn];

bool cmp(hh x,hh y)
{
    return x.x<y.x;
}
void merge_sort(int l,int r,int mid)
{
    int i=l,j=mid+1;
    int k=0;
    while(i<=mid&&j<=r)
    {
        if(c[i]>c[j])
        {
            m[++k]=c[j++];
            ans=(ans+mid-i+1)%mo;
        }
        else 
        {
            m[++k]=c[i];
            i++;
        }
    }
    while(i<=mid) m[++k]=c[i++];
    while(j<=r) m[++k]=c[j++];
    for(int t=l;t<=r;++t)
      c[t]=m[t-l+1]; 
}
void done(int l,int r)
{
    if(r>l)
    {
        int mid=(l+r)>>1;
        done(l,mid);
        done(mid+1,r);
        merge_sort(l,r,mid);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i) 
    {
        scanf("%d",&a[i].x);
        a[i].num=i;
    }
    for(int i=1;i<=n;++i) 
    {
        scanf("%d",&b[i].x);
        b[i].num=i;
    }
    sort(a+1,a+n+1,cmp);
    sort(b+1,b+n+1,cmp);
    for(int i=1;i<=n;++i) fin[i]=a[i].num;
    for(int i=1;i<=n;++i) c[b[i].num]=fin[i];
    done(1,n);

    printf("%d",ans%mo);
    return 0;
}
發佈了77 篇原創文章 · 獲贊 2 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章