P1092 - 【NOIP2013】火柴排队
Description
Input
共三行,第一行包含一个整数 n,表示每盒中火柴的数目。
第二行有 n 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。
第三行有 n 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。
Output
输出共一行,包含一个整数,表示最少交换次数对 99,999,997 取模的结果。
Sample Input
样例1:
4
2 3 1 4
3 2 1 4
样例2:
4
1 3 4 2
1 7 2 4
Sample Output
样例输出1:
1
样例输出2:
2
Hint
样例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 ≤火柴高度≤ 2^31 − 1。
solution
对于此题,很容易证明火柴的位置就是把两列的火柴都排序一下的位置。然后求火柴最少的交换次数(而且是相邻的),所以就是求逆序对,用归并或者树状数组求就行了。
代码如下:
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define INF 0x7ffffff
#define clear(s,z) memset(s,z,sizeof(s))
#define copy(s,z) memcpy(s,z,sizeof(s))
#define mod 99999997
const int maxn=1e5+20;
struct dsl{
int v,mark;
};
int n;
dsl a[maxn],b[maxn],c[maxn];
int tmp[maxn],ans;
int read()
{
char ch;
do{ch=getchar();
}while(ch>'9' || ch<'0');
int sum=0;
do{
sum=sum*10+ch-48;
ch=getchar();
}while(ch>='0'&& ch<='9');
return sum;
}
bool cmp_(const dsl &a,const dsl &b)
{
return a.v<b.v;
}
void init()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)a[i].v=read(),a[i].mark=i;
for(int i=1;i<=n;i++)b[i].v=read(),b[i].mark=i;
sort(a+1,a+n+1,cmp_);
sort(b+1,b+n+1,cmp_);
for(int i=1;i<=n;i++)
c[i].v=a[i].mark,c[i].mark=b[i].mark;
sort(c+1,c+n+1,cmp_);
}
void Merge(int l,int m,int r)
{
int i=l,j=m+1,k=l;
while(i<=m && j<=r)
{
if(c[i].mark>c[j].mark)
{
tmp[k++]=c[j++].mark;
ans=(ans+m-i+1)%mod;
}else
{
tmp[k++]=c[i++].mark;
}
}
while(i<=m)tmp[k++]=c[i++].mark;
while(j<=r)tmp[k++]=c[j++].mark;
for(int i=l;i<=r;i++)
c[i].mark=tmp[i];
}
void Merge_sort(int l,int r)
{
if(l<r)
{
int mid=(l+r)/2;
Merge_sort(l,mid);
Merge_sort(mid+1,r);
Merge(l,mid,r);
}
}
void doing()
{
Merge_sort(1,n);
printf("%d\n",ans);
}
int main()
{
init();
doing();
return 0;
}