JZOJ5958. 【NOIP2018模拟11.8A组】秘密邮件

Description

小 A 收到了一封来自外星球的秘密邮件。邮件由 n(n≤1000000) 个大写英文字母组成,不巧的是小A 收到邮件以后一不小心打乱了原来的字母顺序。但是聪明的小 A 记住了原邮件的完整内容,现在她每次可以选择打乱后的邮件中相邻的两个字母进行交换,问最少交换多少次能够将打乱的邮件恢复成原邮件。

题解

考虑将问题转化为序列上面的问题,
我们将目标串视作是一个有序的序列,而且就认为是1到n。
考虑如何将原串对应,
显然是相同字母的位置相对于,
如果相同字母很多的话,显然是第一个对应第一个,第二个对于第二个,如此类推。
考虑如果第一个对应第二个,第二个对于第一个,这样他们在交换的时候就会有重复的,显然是不优的。
现在考虑如何求最小交换次数,
可以知道,每次交换可以减少一个逆序对的个数,
于是答案就是逆序对个数。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
char ch;
void read(int&n)
{
	for(ch=getchar();ch<'0' || ch>'9';ch=getchar());
	for(n=0;'0'<=ch && ch<='9';ch=getchar())n=(n<<1)+(n<<3)+ch-48;
}
const int N=1000003;
int n,t[26],w[26],a[N],v[26][N],s[N];
long long ans;
int x_(int x){return x&(-x);}
void ins(int x){for(int i=x;i<=n;i=i+x_(i))s[i]++;}
int find(int x){int S=0;for(int i=x;i;i=i-x_(i))S=S+s[i];return S;}
int main()
{
	freopen("letter.in","r",stdin);
	freopen("letter.out","w",stdout);
	read(n);
	for(ch=getchar();ch<'A' || ch>'Z';ch=getchar());
	for(int i=1;i<=n;i++)v[ch-'A'][++t[ch-'A']]=i,ch=getchar();
	for(;ch<'A' || ch>'Z';ch=getchar());
	for(int i=1;i<=n;i++)
	{
		a[i]=v[ch-'A'][++w[ch-'A']],ch=getchar();
		ans=ans+i-find(a[i])-1;ins(a[i]);
	}
	printf("%lld\n",ans);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值