[SZKOpuł sol][POI2009]Elephants
转载请注明出处:http://danihao123.is-programmer.com/
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;
}
评论 (0)