https://codeforces.com/contest/1430/problem/E
题意:交换相邻的字母使得字符串翻转的最小次数。
最近碰到了几道逆序对的题,有个比较明显的特征是交换相邻的数。
思路:
首先并不需要真的移动过去,题目要的只是求次数。
然后考虑一个问题:出现了相同的字母,在交换的时候,目标串的这个字母应该取原串中哪个字母呢?
例子:
原串:abac
目标串:caba.
考虑c从pos:4换到pos:1,后面的aba的相对顺序不变,这时候更优的是拿当前最近的这个a,而不是再去做多余的a的交换。
所以出现了相同字母,用的应该是对应的最近的这个字母。
然后目标串中标出目标串中每个字母对应的转移过来的原串中字母的下标。
原串:
abac
1234
目标串
caba
4123
这个时候按照cf的传统(bushi,可以直接猜一手逆序对了。
这里要转化一下思维,类似这道题:What Goes Up Must Come Down(思维+逆序对构造LIS山峰)
把最终每个数交换的次数转化到,每个数要 被 交换多少次。
看这个caba中的a由于c比a大,对应原串中就是原串的pos=1的a要被c交换一次,b要被c交换一次,pos=3的a要被c交换一次。这里就有3次。
再看caba中的b,可以从原串中发现,pos=3的a要被这个b交换一次。
这样总和是3,对应着的逆序数是3,这个3也就是最终的每个数要对着其他数交换多少次。
代码上注意:开longlong不然会整数溢出wa41.
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=4e5+100;
typedef long long LL;
queue<LL>s[150];
LL a[maxn],tree[maxn],n;
char str[maxn],t[maxn];
void add(LL x,LL d)
{
while(x<=n){
tree[x]+=d;
x+=lowbit(x);
}
}
LL getsum(LL x)
{
LL sum=0;
while(x>0)
{
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
int main(void)
{
scanf("%d",&n);
scanf("%s",str+1);
for(LL i=1;i<=n;i++){
s[str[i]-'0'].push(i);
}
for(LL i=1;i<=n;i++){
t[i]=str[n-i+1];
}
for(LL i=1;i<=n;i++){
a[i]=s[t[i]-'0'].front();
s[t[i]-'0'].pop();
}
LL res=0;
for(LL i=1;i<=n;i++)
{
add(a[i],1);
res+=(i-getsum(a[i]));
}
cout<<res<<endl;
return 0;
}