[SZKOpuł sol][POI2009]Elephants
aji波兰题!(直球)
这个题显然就是对那个排列乘上一个置换,然后置换的每一个循环事独立的,于是分别考虑每一个循环。
然后每一个循环有一种显然的构造方法就是从一个点开始,逆着边不停的交换两个点。这样的话有一个点会对答案做\(s - 1\)次贡献(\(s\)为循环大小),其他点都会只做一次循环。显然那个点选权值最小的点事坠吼的。
还有一种构造就是把全局最小值和循环最小值换一下,然后用上面的手段构造,最后再把全局最小值和循环最小值换回来。
代码:
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <utility> #include <vector> const int maxn = 1000005; int next[maxn]; using ll = long long; ll m[maxn]; int A[maxn], B[maxn], ma[maxn]; bool vis[maxn]; int main() { int n; scanf("%d", &n); for(int i = 1; i <= n; i ++) { scanf("%lld", &m[i]); } for(int i = 1; i <= n; i ++) { scanf("%d", &A[i]); ma[A[i]] = i; } for(int i = 1; i <= n; i ++) { scanf("%d", &B[i]); next[ma[B[i]]] = i; } ll ans = 0LL; ll tv = *std::min_element(m + 1, m + 1 + n); for(int i = 1; i <= n; i ++) { if(next[i] != i && !vis[i]) { int p = i; ll sumv = 0LL, minv = 0x7fffffffLL; ll cnt = 0; do { vis[p] = true; cnt ++; sumv += m[A[p]]; minv = std::min(minv, m[A[p]]); p = next[p]; } while(p != i); ll delta = sumv + std::min(minv * (cnt - 2LL), minv + tv * (cnt + 1LL)); ans += delta; } } printf("%lld\n", ans); return 0; }