题目链接:https://codeforces.com/contest/665/problem/D
题目大意:
给出一个字符串s
,将字符串s
倒置后形成字符串t
每次可以对字符串s
进行操作,每次可以交换相邻的两个字符
询问最少交换多少次可以使得s->t
题目思路:
备战CCPC刷题的时候的遗留问题
首先将字符串s
倒置之后形成字符串t
交换操作首先考虑逆序对:
将一段序列进行冒泡排序,进行相邻交换,最后的交换次数最少为:逆序对总和
那么可以这么考虑这个题:
令最终状态 t 的每个位置成为最后的有序序列
abcde -> 12345
aabbb -> 12345
第一个例子来看,翻转之后成为54321,那么逆序对就是答案了
第二个例子来看,翻转之后成为54321,但此时逆序对并不是答案
,为什么呢?
因为,此时aa是可以不需要交换的,也就是说aa是同一个元素,不需要考虑谁前谁后。
所以,把同一个元素内部进行排序,即第二个例子反转后成为:45123
这样就可以了。
另外还有一种树状数组直接维护,当前位置前有几个数的写法,思路也比较容易懂,大概分两步:
1.记录每个字母的出现位置
2.每次让最近的字母移动到当前位置,过程用树状数组维护
Code:
/*** keep hungry and calm CoolGuang! ***/
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define d(x) printf("%lld\n",x);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll INF= 1e17;
const ll maxn = 4e5+700;
const int mod= 1e9+7;
const int up = 1e9;
template<typename T>inline void read(T &a){
char c=getchar();T x=0,f=1;while(!isdigit(c)){
if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){
x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
ll n,m,p;
ll num[maxn];
vector<int>v[30];
char s[maxn];
int c[maxn];
ll sum[maxn];
void add(int pos){
while(pos<=n){
sum[pos]++;
pos += pos&-pos;
}
}
ll GetSum(int pos){
ll ans = 0;
while(pos){
ans += sum[pos];
pos -= pos&-pos;
}return ans;
}
int main(){
///11123
///32111
read(n);
scanf("%s",s+1);
for(int i=1;i<=n;i++)
v[s[i]-'a'].push_back(i);
for(int i=0;i<26;i++){
int sz =v[i].size();
for(int l = 0,r = sz-1;l<sz;l++,r--){
int x = v[i][l];
c[n-x+1] = v[i][r];
}
}
ll ans = 0;
for(int i=1;i<=n;i++){
ans += GetSum(n)-GetSum(c[i]);
add(c[i]);
}
printf("%lld\n",ans);
return 0;
}
/***
aaaza
azaaa
****/