【NOIP2013提高組】火柴排隊
題目背景
NOIP2013 提高組 Day1 試題
題目描述
涵涵有兩盒火柴,每盒裝有 n 根火柴,每根火柴都有一個高度。現在將每盒中的火柴各自排成一列,同一列火柴的高度互不相同,兩列火柴之間的距離定義為:
其中 ai 表示第一列火柴中第 i個火柴的高度,bi 表示第二列火柴中第 i 個火柴的高度。
每列火柴中相鄰兩根火柴的位置都可以交換,請你通過交換使得兩列火柴之間的距離最小。請問得到這個最小的距離,最少需要交換多少次?如果這個數字太大,請輸出這個最小交換次數對 99,999,997 取模的結果。
輸入格式
共三行,第一行包含一個整數 n,表示每盒中火柴的數目。 第二行有 n 個整數,每兩個整數之間用一個空格隔開,表示第一列火柴的高度。 第三行有 n 個整數,每兩個整數之間用一個空格隔開,表示第二列火柴的高度。
輸出格式
輸出共一行,包含一個整數,表示最少交換次數對 99,999,997 取模的結果。
樣例資料 1
輸入
4 2 3 1 4 3 2 1 4
輸出
1
樣例資料 2
輸入
4 1 3 4 2 1 7 2 4
輸出
2
備註
【樣例1說明】 最小距離是 0,最少需要交換 1 次,比如:交換第 1 列的前 2 根火柴或者交換第 2 列的前 2 根火柴。
【樣例2說明】 最小距離是 10,最少需要交換 2 次,比如:交換第 1 列的中間 2 根火柴的位置,再交換第 2 列中後 2 根火柴的位置。
【資料範圍】 對於 10% 的資料, 1≤n≤10; 對於 30% 的資料,1≤n≤100; 對於 60% 的資料,1≤n≤1,000; 對於 100% 的資料,1≤n≤100,000 ;0≤火柴高度≤23^1-1
解析:
想錯了一個地方卡了半天。。。
首先我們知道要使得原式最小,只有在兩列數的第K大相互對應的時候最小(證明略)。
那麼問題就轉化成了:
把數列b的數字大小關係移動成數列a的大小關係所需要的最小移動次數。
如何做呢?舉個例子:
3 2 4 1
2 4 1 3
現在要求最小移動次數是的數列b變為:3 2 4 1
那麼說明原位置1要移動大2,2移動到3,3移動到4,4移動到1(這裡的數字指位置)。對應到一個新數列中即為:
2 3 4 1
答案就為讓這個新序列有序的最小移動次數,於是就是裸的逆序對了,用樹狀陣列或歸併排序均可。
程式碼(歸併排序):
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <cctype>
#include <ctime>
#include <queue>
using namespace std;
const int Max=100105;
const int mod=99999997;
struct px{int bh,num;};
px a[Max],b[Max];
long long c[Max],r[Max];
long long n,sum;
int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(c<'0'||c>'9')&&(c!='-');c=getchar());
if(c=='-') {f=-1; c=getchar();}
for(;c>='0'&&c<='9';c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
bool comp(const px &a,const px &b)
{
return a.num<b.num;
}
void merge(int s,int mid,int t)
{
int i=s,k=s,j=mid+1;
while(i<=mid&&j<=t)
{
if(c[i]>c[j])
{
r[k++]=c[j++];
sum+=mid-i+1;
}
else r[k++]=c[i++];
}
while(i<=mid) r[k++]=c[i++];
while(j<=t) r[k++]=c[j++];
for(int x=s;x<=t;x++) c[x]=r[x];
}
void mergesort(int s,int t)
{
if(s<t)
{
int mid=(s+t)/2;
mergesort(s,mid);
mergesort(mid+1,t);
merge(s,mid,t);
}
}
int main()
{
n=get_int();
for(int i=1;i<=n;i++)
{
a[i].num=get_int();
a[i].bh=i;
}
for(int i=1;i<=n;i++)
{
b[i].num=get_int();
b[i].bh=i;
}
sort(a+1,a+n+1,comp);
sort(b+1,b+n+1,comp);
for(int i=1;i<=n;i++) c[b[i].bh]=a[i].bh;
mergesort(1,n);
cout<<sum%mod<<endl;
return 0;
}
程式碼(樹狀陣列):
#include <bits/stdc++.h>
using namespace std;
const int mod=99999997;
const int Max=100005;
int n,m,tot,ans;
int sum[Max],c[Max<<1],num[Max];
struct shu{int num,id;};
shu a[Max],b[Max];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='0') f=-1,c=getchar();
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline bool comp(const shu &a,const shu &b){return a.num<b.num;}
inline void pre()
{
sort(c+1,c+tot+1);
m=unique(c+1,c+tot+1)-c-1;
for(int i=1;i<=n;i++) a[i].num=lower_bound(c+1,c+m+1,a[i].num)-c;
for(int i=1;i<=n;i++) b[i].num=lower_bound(c+1,c+m+1,b[i].num)-c;
}
inline void add(int num)
{
for(int i=num;i<=100000;i+=i&(-i)) sum[i]++;
}
inline int Q(int num)
{
int ans=0;
for(int i=num;i;i-=i&(-i)) ans+=sum[i];
return ans;
}
inline void solve()
{
for(int i=1;i<=n;i++) add(num[i]),ans=(ans+i-Q(num[i]))%mod;
cout<<ans<<"\n";
}
int main()
{
n=get_int();
for(int i=1;i<=n;i++) c[++tot]=a[i].num=get_int(),a[i].id=i;
for(int i=1;i<=n;i++) c[++tot]=b[i].num=get_int(),b[i].id=i;
pre();
sort(a+1,a+n+1,comp),sort(b+1,b+n+1,comp);
for(int i=1;i<=n;i++) num[a[i].id]=b[i].id;
solve();
return 0;
}