Description:
给定一个序列 (a_i) ,每次可以交换相邻两个元素,求使序列变成若干个极大连续段,每个极大连续段内部的值相同且任意两个极大连续段的值互不相同。
(nle 4 imes 10^5, a_ile 20)
Solution:
由于值域很小,启发我们从值域入手,考虑每一种颜色。
设 (cnt[i][j]) 表示在只考虑颜色 (i) 和 (j) 的情况下,把所以颜色 (i) 都移到所有颜色 (j) 的前面的步数,这个对每一个颜色用 ( ext{vector}) 存下出现的位置(从小到大),用 ({ m two pointers}) 扫一下即可求得 。
考虑状压每一种颜色,二进制下为1表示这个位的颜色已经被安排好了。
设 (dp[S]) 表示 (S) 的颜色已经安排好在序列的前面且两两不交的最小步数,那么转移就枚举一个不在 (S) 中的点 (i) ,把 (i) 放在考虑完的 (S) 的后面,那么产生的步数就是把 (S) 放在 (i) 的前面的步数,通过 (cnt[][i]) 可以求得。
Code:
#include <iostream>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>
typedef long long LL;
typedef unsigned long long uLL;
#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define MP(x, y) std::make_pair(x, y)
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define GO cerr << "GO" << endl;
using namespace std;
inline void proc_status()
{
ifstream t("/proc/self/status");
cerr << string(istreambuf_iterator<char>(t), istreambuf_iterator<char>()) << endl;
}
template<class T> inline T read()
{
register T x(0);
register char c;
register int f(1);
while (!isdigit(c = getchar())) if (c == '-') f = -1;
while (x = (x << 1) + (x << 3) + (c xor 48), isdigit(c = getchar()));
return x * f;
}
template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
const int maxN = 4e5;
const int maxM = 20;
int n;
vector<int> col[maxM + 1];
LL cnt[maxM + 1][maxM + 1];
LL dp[1 << maxM];
void Input()
{
n = read<int>();
for (int i = 1; i <= n; ++i)
{
int x = read<int>();
col[x].push_back(i);
}
}
void Init()
{
for (int i = 1; i <= 20; ++i)
for (int j = 1; j <= 20; ++j)
if (i != j)
{
if (!col[i].size() || !col[j].size()) continue;
int l = 0;
for (int k = 0; k < SZ(col[i]); ++k)
{
while (true)
{
if (l == SZ(col[j]) - 1 || col[j][l + 1] > col[i][k])
break;
l++;
}
if (col[j][l] < col[i][k])
cnt[i][j] += l + 1;
}
}
}
void Solve()
{
memset(dp, 0x3f, sizeof dp);
dp[0] = 0;
for (int S = 0; S < 1 << maxM; ++S)
{
for (int i = 0; i < maxM; ++i)
if (!(S >> i & 1))
{
LL sum(0);
for (int j = 0; j < maxM; ++j)
if (S >> j & 1)
sum += cnt[j + 1][i + 1];
chkmin(dp[S | (1 << i)], dp[S] + sum);
}
}
cout << dp[(1 << maxM) - 1] << endl;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("CF585E.in", "r", stdin);
freopen("CF585E.out", "w", stdout);
#endif
Input();
Init();
Solve();
return 0;
}