CF1526D结论/树状数组/逆序对性质

题目

D. Kill Anton

给出一个只由ANTO四个字符组成的字符串,你需要对其进行重排列,使得新字符串转化成原字符串的操作数最小。一次操作可以交换字符串中两个相邻的字符。

解题思路

只能交换字符串中两个相邻的字符时,将一个字符串的排列转化为另一个排列的最小操作数可见:CF1430E贪心、树状数组

这题有个比较复杂的结论,重排列的字符串一定是同字符连续的,(不会证明),也就是只有 4 ! 4! 4!种方案。只需枚举每一种,用上面的方法取出max即可。

Solution II

由于只有四种字符,我们可以用逆序对的性质优化时间复杂度。

  1. 逆序对为奇数的排列我们称之为奇排列。
  2. 对换改变排列的奇偶性。

应用到这题,交换相邻位置,逆序对的数量要么 + 1 +1 +1,要么 − 1 -1 1,要么不变。若将重排列的字符串四种字符看做1234,那么就要把原字符串的逆序对数量减至0。而每一次交换相邻的字符,我们肯定会减少一个逆序对,因此就是求原字符串的逆序对数量,这样时间复杂度少了一个 l o g log log
虽然 CF上跑的比第一种带 l o g log log的还慢,应该是我写的太烂了

代码

Solution I 线段树、树状数组

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
deque<int> q[30];
int c[N];
int n;
void add(int i, int val)
{
    while (i <= n)
    {
        c[i] += val;
        i += i & -i;
    }
}
int sum(int i)
{
    int sum = 0;
    while (i)
    {
        sum += c[i];
        i -= i & -i;
    }
    return sum;
}

char s[N];
char s2[N];
char ANS[N];

char res[] = {'A', 'N', 'O', 'T'};

map<char, int> mp;
void solve()
{
    mp.clear();
    scanf("%s", s2 + 1);
    n = strlen(s2 + 1);
    for (int i = 1; i <= n; i++)
        mp[s2[i]]++;
    long long ans = -1;
    do
    {
        int ct = 0;
        for (int i = 0; i < 4; i++)
        {
            int num = mp[res[i]];
            while (num--)
                s[++ct] = res[i];
        }
        long long res = 0;
        for (int i = 1; i <= n; i++)
            c[i] = 0;
        for (int i = 1; i <= n; i++)
            add(i, 1);
        for (int i = 1; i <= n; i++)
            q[s2[i] - 'A'].push_back(i);
        for (int i = 1; i <= n; i++)
        {
            int pos = q[s[i] - 'A'].front();
            q[s[i] - 'A'].pop_front();
            res += sum(pos) - 1;
            add(pos, -1);
        }
        if (res > ans)
        {
            ans = res;
            for (int i = 1; i <= n; i++)
                ANS[i] = s[i];
        }
    } while (next_permutation(res, res + 4));
    for (int i = 1; i <= n; i++)
        cout << ANS[i];
    cout << endl;
}
int main()
{
    int t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

Solution II 逆序对

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
deque<int> q[30];
int n;

char s[N];
char s2[N];
char ANS[N];

char res[] = {'A', 'N', 'O', 'T'};
map<char, int> mp;
map<char, int> rk;

long long f(char s1[], char s2[])
{
    long long ans = 0;
    int cnt[5];
    for (int i = 0; i < 4; i++)
        cnt[i] = 0, rk[res[i]] = i;

    for (int i = 1; i <= n; i++)
    {
        int val = rk[s2[i]];
        for (int j = val + 1; j < 4; j++)
            ans += cnt[j];
        cnt[val]++;
    }
    return ans;
}
void solve()
{
    mp.clear();
    scanf("%s", s2 + 1);
    n = strlen(s2 + 1);
    for (int i = 1; i <= n; i++)
        mp[s2[i]]++;
    long long ans = -1;
    do
    {
        int ct = 0;
        for (int i = 0; i < 4; i++)
        {
            int num = mp[res[i]];
            while (num--)
                s[++ct] = res[i];
        }
        long long res = f(s, s2);
        if (res > ans)
        {
            ans = res;
            for (int i = 1; i <= n; i++)
                ANS[i] = s[i];
        }
    } while (next_permutation(res, res + 4));
    for (int i = 1; i <= n; i++)
        cout << ANS[i];
    cout << endl;
}
int main()
{
    int t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hesorchen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值