(Description)
(Smart)收到了一封来自外星球的秘密邮件。邮件由(n)个大写英文字母组成,不巧的是(Smart)收到邮件以后一不小心打乱了原来的字母顺序。但是聪明的(Smart)记住了原邮件的完整内容,现在他每次可以选择打乱后的邮件中相邻的两个字母进行交换,问最少交换多少次能够将打乱的邮件恢复成原邮件。
(Input)
第一行(1)个整数(n)代表邮件长度。
第二行长度为(n)的只包含大写字母的字符串表示打乱后的邮件。
第三行长度为(n)的只包含大写字母的字符串表示原邮件。
为了保证打乱后的邮件可以恢复成原邮件,所有测试数据满足任意一种大写字母在两封邮件中的出现次数相同。
(Output)
输出一个整数表示最少的交换次数。
(Sample Input 1)
4
ABCD
DBCA
(Sample Output 1)
5
(Hint)
【数据范围】
(40%)的数据(:n≤30);
另外(20%)的数据(:n=5000;)
(100%)的数据(:n≤1000000)。
(Solution)
很明显的逆序对......
把打乱的邮件中的大写字母一一编号,然后把它们映射到原邮件。
这里我们会遇到相同的大写字母出现多次,那么我们考虑将大写字母出现较早的先映射
就像样例一样
(ABCD)
(DBCA)
(1 2 3 4)
映射后就是
(4 2 3 1)
然后答案就是求(4 2 3 1)这个序列的逆序对个数
((4,2)) ((4,3)) ((4,1)) ((2,1)) ((3,1))
求逆序对的个数可以用树状数组(or)归并排序......
这里用的是归并排序
#include<bits/stdc++.h>
#define Re register int
using namespace std;
vector<long long> W[27];
long long N,a[1000050],b[1000050],Now[27];
long long Ans;
char S_Mess[1000050],S_Last[1000050];
inline void merge_sort(int l,int r){
if(r-l>0){
int mid=(l+r)>>1;
int Wh=l;
int p=l,q=mid+1;
merge_sort(l,mid);
merge_sort(mid+1,r);
while(p<=mid || q<=r){
if(q>r || (p<=mid && a[p]<=a[q])) b[Wh++]=a[p++];
else{
b[Wh++]=a[q++];
Ans+=mid-p+1;
}
}
for(Re i=l; i<=r; i++) a[i]=b[i];
}
}
int main(){
scanf("%d",&N);
scanf("%s",S_Mess+1);
scanf("%s",S_Last+1);
for (Re i=1; i<=N; ++i) W[S_Last[i]-'A'+1].push_back(i);
for (Re i=1; i<=N; ++i) a[i]=W[S_Mess[i]-'A'+1][Now[S_Mess[i]-'A'+1]++];
merge_sort(1,N);
printf("%lld",Ans);
return 0;
}