给定一个序列 \(a_i\) ,每次能够交换相邻两个元素,求使序列变成若干个极大连续段,每一个极大连续段内部的值相同且任意两个极大连续段的值互不相同。ios
\(n\le 4\times 10^5, a_i\le 20\)git
因为值域很小,启发咱们从值域入手,考虑每一种颜色。spa
设 \(cnt[i][j]\) 表示在只考虑颜色 \(i\) 和 \(j\) 的状况下,把因此颜色 \(i\) 都移到全部颜色 \(j\) 的前面的步数,这个对每个颜色用 \(\text{vector}\) 存下出现的位置(从小到大),用 \({\rm two\ pointers}\) 扫一下便可求得 。code
考虑状压每一种颜色,二进制下为1表示这个位的颜色已经被安排好了。ip
设 \(dp[S]\) 表示 \(S\) 的颜色已经安排好在序列的前面且两两不交的最小步数,那么转移就枚举一个不在 \(S\) 中的点 \(i\) ,把 \(i\) 放在考虑完的 \(S\) 的后面,那么产生的步数就是把 \(S\) 放在 \(i\) 的前面的步数,经过 \(cnt[][i]\) 能够求得。get
#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; }