1 条题解
-
0
一道不算难的状压。
根据期望的线性性,将组 上船的代价拆成 内部所有人之间的代价以及其它所有已经上船的组对 产生的代价。
对于组 而言,必然存在一个分界点 满足位置 的人从左边上船, 的人从右边上船。考虑如果 且 从右边上船, 从左边上船,容易证明将它们调整为 从左边上船, 从右边上船代价必然减小。
假设在 处分割时从左边上船的人数为 ,则从右边上船的人数为 。考虑任意两对从左边上船的人,当位置在左边的人先于位置在右边的人上船时,会产生 的代价。因为随机排列中某两个数具有特定的前后顺序的概率相等,均为 ,所以分割方案 的组内贡献为 。不妨改变 的定义为在 处分割,也就是前 个人从左边上船,后 个人从右边上船,则 是关于 的二次函数,且具有下凸性。
考虑某一组 对 的贡献。如果 从左边上船,那么它的贡献是 的 的数量,从右边同理。因此,预处理出 表示如果在 处分割,那么 对在左边上船的 的总贡献, 表示 对在右边上船的 的总贡献。预处理枚举任意两组和分隔位置,时间复杂度 或 ,视实现细节程度而定。
考虑 和 ,因为 的 的数量显然不小于 的 的数量,所以 具有下凸性。同理 具有下凸性。
因为我们只关心有哪些组此时已经上船,而非它们具体的上船顺序,自然考虑状压 DP 表示上船的组的集合为 时的最小代价。转移枚举不属于 的组 ,然后每个可能的分割点以及每个 ,容易计算贡献。这样暴力的时间复杂度为 (有个 可以被均摊分析掉,但是没啥影响),无法接受。
但注意到 ,, 均下凸,所以贡献单峰(显然,下凸是比单峰更强的条件),三分最优分割点位置即可。时间复杂度 。
#include <bits/stdc++.h> using namespace std; bool Mbe; constexpr int N = 1e5 + 5; template <class T> inline void cmin(T &x, T y) {x = x < y ? x : y;} int n, mx; char s[N]; vector<int> buc[N]; long long f[15][15][N], g[15][15][N]; long long ans[1 << 15]; bool Med; int main() { fprintf(stderr, "%.3lf\n", (&Mbe - &Med) / 1048576.0); // freopen("passes.in", "r", stdin); // freopen("passes.out", "w", stdout); scanf("%s", s + 1); n = strlen(s + 1); for(int i = 1; i <= n; i++) { mx = max(mx, (int) (s[i] - 'A') + 1); buc[s[i] - 'A'].push_back(i); } for(int i = 0; i < mx; i++) for(int j = 0; j < mx; j++) if(i != j) { for(int k = 1, cur = 0; k <= n; k++) { f[i][j][k] = f[i][j][k - 1]; if(s[k] == i + 'A') cur++; if(s[k] == j + 'A') f[i][j][k] += cur; } for(int k = n, cur = 0; k; k--) { g[i][j][k] = g[i][j][k + 1]; if(s[k] == i + 'A') cur++; if(s[k] == j + 'A') g[i][j][k] += cur; } } memset(ans, 0x3f, sizeof(ans)); ans[0] = 0; for(int i = 0; i < 1 << mx; i++) { for(int j = 0; j < mx; j++) if(i >> j & 1 ^ 1) { int l = 0, r = buc[j].size(); auto calc = [&](int p) { int pos = p ? buc[j][p - 1] : 0; int back = buc[j].size() - p; long long tot = 1ll * p * (p - 1) + 1ll * back * (back - 1); tot >>= 1; for(int k = 0; k < mx; k++) if(i >> k & 1) tot += f[k][j][pos] + g[k][j][pos + 1] << 1; return tot; }; while(l + 2 < r) { int m1 = l + r >> 1, m2 = m1 + 1; if(calc(m1) <= calc(m2)) r = m2; else l = m1; } long long coef = 1e18; for(int p = l; p <= r; p++) cmin(coef, calc(p)); cmin(ans[i | (1 << j)], ans[i] + coef); } } printf("%.3lf\n", ans[(1 << mx) - 1] * .5); return 0; }
- 1
信息
- ID
- 7272
- 时间
- 2000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者