【2019国庆集训day2-s】秘密邮件
\(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;
}