1. 程式人生 > >【NOIP2013提高組】火柴排隊

【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;
}