[POI2009]SLO-Elephants

首先可以发现我们可以发现最终状态和初始状态都是一个大小为 \(n\) 的排列,且恰好有 \(n\) 种关系,于是我们对于每个 \(a_i\) 连一条 \(a_i \rightarrow b_i\) 的有向边,这张图的形态将会相对简单。

因为每个位置最终变成的数不同,因此这张图将会是由若干个简单环构成。如果环的大小为 \(1\) 则不需要交换,如果环的大小为 \(2\) 则直接交换即可,下面我们考虑环的大小至少为 \(3\) 的情况。

不难发现我们要做的相当于把环上的每个点向前挪一个,我们先考虑只交换环内点,那么最小的方案就会是从某个点开始一直往下不断交换,令 \(S\) 为环上点权值之和,\(len\) 为环的大小,那么这样的代价就会是 \(S + (len - 2) \times w_i\) 那么我们只需要选择一个最小的 \(w_i\) 上去交换一圈即可。如果考虑可以和环外的点交换,必然是选择环内环外权值最小的两个点交换,再由环外最小的点在环内交换一圈,实际上每次环外权值最小的点交换完后我们可以直接和原来的交换回来,因为如果我们不交换回来,那么最终这小和它交换的点可以构成一个新的环,那么还需要再交换回来一次,这样的代价是一样的,为了方便起见我们直接交换回来即可。

#include<bits/stdc++.h>
using namespace std;
#define N 1000000 + 5
#define inf 1000000000
#define int long long
#define rep(i, l, r) for(int i = l; i <= r; ++i)
bool book[N];
int n, sum, len, ans, M, Min, w[N], a[N], b[N], nxt[N];
int read(){
    char c; int x = 0, f = 1;
    c = getchar();
    while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
void dfs(int u){
    if(book[u]) return; book[u] = true;
    Min = min(Min, w[u]), sum += w[u], ++len;
    dfs(nxt[u]);
}
signed main(){
    n = read(), M = inf;
    rep(i, 1, n) w[i] = read(), M = min(M, w[i]);
    rep(i, 1, n) a[i] = read();
    rep(i, 1, n) b[i] = read();
    rep(i, 1, n) nxt[a[i]] = b[i];
    rep(i, 1, n) if(!book[i]){
        Min = inf, sum = 0, len = 0;
        dfs(i);
        if(len == 1) continue;
        if(sum + (len - 2) * Min < Min + sum + (len + 1) * M) ans += sum + (len - 2) * Min;
        else ans += Min + sum + (len + 1) * M;
    }
    printf("%lld", ans);
    return 0;
}
posted @ 2020-08-04 11:37  Achtoria  阅读(80)  评论(0编辑  收藏  举报