cf edu E string reversal(思维(逆序对)+树状数组求逆序对)

传送门
You are given a string s. You have to reverse it — that is, the first letter should become equal to the last letter before the reversal, the second letter should become equal to the second-to-last letter before the reversal — and so on. For example, if your goal is to reverse the string “abddea”, you should get the string “aeddba”. To accomplish your goal, you can swap the neighboring elements of the string.

Your task is to calculate the minimum number of swaps you have to perform to reverse the given string.

Input
The first line contains one integer n (2≤n≤200000) — the length of s.

The second line contains s — a string consisting of n lowercase Latin letters.

Output
Print one integer — the minimum number of swaps of neighboring elements you have to perform to reverse the string.

Examples
input
5
aaaza
output
2
input
6
cbaabc
output
0
input
9
icpcsguru
output
30
Note
In the first example, you have to swap the third and the fourth elements, so the string becomes “aazaa”. Then you have to swap the second and the third elements, so the string becomes “azaaa”. So, it is possible to reverse the string in two swaps.

Since the string in the second example is a palindrome, you don’t have to do anything to reverse it.
大致翻译:
你得到了一个字符串s。你必须把它反转,也就是说,第一个字母应该等于倒转前的最后一个字母,第二个字母应该等于倒转前的第二个到最后一个字母,等等。例如,如果您的目标是反转字符串“abdea”,那么您应该得到字符串“aedba”。为了实现目标,可以交换字符串的相邻元素。

您的任务是计算您必须执行的反转给定字符串的最小交换数。

输入

第一行包含一个整数n(2≤n≤200000)-s。

第二行包含s-一个由n个小写拉丁文字母组成的字符串。

输出

打印一个整数—要反转字符串,必须执行的相邻元素的最小交换数。

注意
在第一个示例中,您必须交换第三个和第四个元素,这样字符串就变成了“aazaa”。然后,您必须交换第二和第三个元素,这样字符串就变成“azaa”。因此,可以在两个交换中反转字符串。

由于第二个示例中的字符串是回文,所以您不必做任何事情来反转它。

思路:
将字符串每个字母所在位置下标记录下来;
翻转所形成的字符串每次都取此时离得最近的相同字母,组成的一串数字(即翻转后每个字母对应位置)后求逆序对个数,即需要交换的次数(即为题目所求)

求逆序对的思路:每次查找比当前数字大的数字出现1 的个数(或者说是出现的次数),然后在当前数字上标记为1;重复此操作,直到这串数字都找一遍为止,若直接查找需要O(n2)的时间复杂度,当数字多起来就会爆,所以我们可以用树状数组来进行更新、求和的操作。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
//const int maxx=0x3f3f3f3f;
const int N=2e5+10;
//const ll n=20210411;
//const ll mod=1e9+7;
queue<int>pos[30];
char s[N],reve[N];//s为原字符串,reve为翻转后的字符串
int a[N],c[N];//a为原数组,c为树状数组
int n;
//用while或for的两种写法
inline void update(int x){
//	for(;x<=n;x+=x&-x) c[x]++;
	while(x<=n){
		c[x]++;
		x+=x&-x;
	}
}
inline int ask(int x){
	int ans=0;
//	for(;x;x-=x&-x)ans+=c[x];
	while(x){
		ans+=c[x];
		x-=x&-x;
	}
	return ans;
}
int main()
{
//    ios_base::sync_with_stdio(false);
	
	scanf("%d%s",&n,s+1);
	for(int i=1;i<=n;i++){
		pos[s[i]-'a'].push(i);
		reve[n-i+1]=s[i];//翻转字符串 
	}
	for(int i=1;i<=n;i++){//c为组成新字符串后字符(原下标)所在的位置 
		a[i]=pos[reve[i]-'a'].front();
		pos[reve[i]-'a'].pop();
	}
	ll ans=0;
	for(int i=n;i;i--){//运用树状数组求逆序对
		ans+=ask(a[i]);
		update(a[i]);
	}
	cout<<ans<<endl;
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值