【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;
}
posted @ 2019-10-02 17:26  to_the_end  阅读(362)  评论(0编辑  收藏  举报