CF1526D结论/树状数组/逆序对性质
Posted hesorchen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF1526D结论/树状数组/逆序对性质相关的知识,希望对你有一定的参考价值。
题目
给出一个只由ANTO四个字符组成的字符串,你需要对其进行重排列,使得新字符串转化成原字符串的操作数最小。一次操作可以交换字符串中两个相邻的字符。
解题思路
只能交换字符串中两个相邻的字符时,将一个字符串的排列转化为另一个排列的最小操作数可见:CF1430E贪心、树状数组。
这题有个比较复杂的结论,重排列的字符串一定是同字符连续的,(不会证明),也就是只有
4
!
4!
4!种方案。只需枚举每一种,用上面的方法取出max即可。
Solution II
由于只有四种字符,我们可以用逆序对的性质优化时间复杂度。
- 逆序对为奇数的排列我们称之为奇排列。
- 对换改变排列的奇偶性。
应用到这题,交换相邻位置,逆序对的数量要么
+
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;
}
以上是关于CF1526D结论/树状数组/逆序对性质的主要内容,如果未能解决你的问题,请参考以下文章