DestinHistoire

 

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  阅读(72)  评论(0编辑  收藏  举报

导航