BZOJ 1119 - [POI2009]SLO
Published on 2016-09-21描述
对于一个 的排列 ,每次你可以交换两个数 与 ,代价为 ,若干次交换的代价为每次交换的代价之和。请问将 变为 所需的最小代价是多少。
分析
我们将 乘上 这个置换的逆,这样就使得 变为 。
也就成为了一个置换了,我们就是要通过交换,来实现这个置换,根据常用方法,将 分解为循环。对于循环 ,设长度为 ,可以发现至少要进行 次交换才能达到目标,不妨用代价最小的元素与其余 个元素各交换一次,这样代价为 , 为这个循环中的代价和, 为这个循环中的最小值。
对每个循环独立处理,是不是就能得到最优解了呢?并不一定,我们可以将 中的最小元素 先与全局的最小元素 调换,然后用 进行 次交换,接着换回 与 ,这样的代价为 。
于是每个循环都可以选择是否换进最小值(不用担心能不能成立,我们只需要安排含有最小值的循环最后操作即可),答案为:
代码
// Created by Sengxian on 09/20/16. // Copyright (c) 2016年 Sengxian. All rights reserved. // BZOJ 1119 置换 + 贪心 #include <bits/stdc++.h> using namespace std; typedef long long ll; inline int ReadInt() { static int n, ch; n = 0, ch = getchar(); while (!isdigit(ch)) ch = getchar(); while (isdigit(ch)) n = (n << 3) + (n << 1) + ch - '0', ch = getchar(); return n; } const int MAX_N = 1000000 + 3; int n, _w[MAX_N], w[MAX_N], a[MAX_N], b[MAX_N], mark[MAX_N]; bool vis[MAX_N]; int main() { #ifndef ONLINE_JUDGE freopen("test.in", "r", stdin); #endif n = ReadInt(); ll ans = 0; int m = INT_MAX; for (int i = 0; i < n; ++i) ans += _w[i] = ReadInt(), m = min(m, _w[i]); for (int i = 0; i < n; ++i) a[i] = i, mark[ReadInt() - 1] = i; for (int i = 0; i < n; ++i) w[mark[i]] = _w[i]; for (int i = 0; i < n; ++i) b[i] = mark[ReadInt() - 1]; for (int i = 0; i < n; ++i) if (!vis[i]) { int u = i, sz = 0, _mn = INT_MAX; do { vis[u] = true; sz++, _mn = min(_mn, w[u]); u = b[u]; } while (!vis[u]); ans += min((ll)(sz - 2) * _mn, _mn + (ll)(sz + 1) * m); } printf("%lld\n", ans); return 0; }