BZOJ-2789 [Poi2012]Letters(树状数组+逆序对)
题目描述
给出两个长度相同(长度 \(\leq 10^6\))且由大写英文字母组成的字符串 \(s\)、\(t\),保证 \(s\) 和 \(t\) 中每种字母出现的次数相同。现在每次可以交换 \(s\) 中相邻两个字符,求最少需要交换多少次可以使得 \(s\) 变成 \(t\)。
分析
有一个结论:串 \(s\) 交换相邻字符变成串 \(t\),需要的最少次数为串 \(s\) 中元素在 串 \(t\) 中的位置组成的序列的逆序对数,即令 temp[a[i].ID]=b[i].ID
。
由于串中的字符可能是相同的,所以把字符大小作为第一关键字,字符位置作为第二关键字排序。比如:\(s=ABABA,t=BAABA\),编号后即为 \([a_1,b_1,a_2,b_2,a_3]\) 和 \([b_1,a_1,a_2,b_2,a_3]\),转化为数字编号即为 $[1,2,3,4,5] $ 和 $[2,1,3,4,5] $,后者的逆序对数恰好为 \(1\),答案为 \(temp\) 数组的逆序对数。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
struct node
{
char val;
int ID;
}a[N],b[N];
bool cmp(node A,node B)
{
if(A.val==B.val)
return A.ID<B.ID;
return A.val<B.val;
}
int n;
char s[N],t[N];
long long temp[N],c[N];
int lowbit(int x)
{
return x&-x;
}
long long query(long long x)
{
long long ans=0;
while(x)
{
ans=ans+c[x];
x=x-lowbit(x);
}
return ans;
}
void add(long long x,long long val)
{
while(x<=n)
{
c[x]+=val;
x=x+lowbit(x);
}
}
int main()
{
cin>>n;
scanf("%s",s+1);
for(int i=1;i<=n;i++)
{
a[i].val=s[i]-'A';
a[i].ID=i;
}
sort(a+1,a+1+n,cmp);
scanf("%s",t+1);
for(int i=1;i<=n;i++)
{
b[i].val=t[i]-'A';
b[i].ID=i;
}
sort(b+1,b+1+n,cmp);
for(int i=1;i<=n;i++)
temp[a[i].ID]=b[i].ID;
long long ans=0;
for(int i=1;i<=n;i++)
{
ans=ans+query(n)-query(temp[i]);
add(temp[i],1);
}
cout<<ans<<endl;
return 0;
}
posted on 2020-12-11 21:44 DestinHistoire 阅读(74) 评论(0) 编辑 收藏 举报