給定一個序列 \(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; }