1119: [POI2009]SLO
Description
对于一个1-N的排列(ai),每次你可以交换两个数ax与ay(x<>y),代价为W(ax)+W(ay) 若干次交换的代价为每次交换的代价之和。请问将(ai)变为(bi)所需的最小代价是多少。
Input
第一行N。第二行N个数表示wi。第三行N个数表示ai。第四行N个数表示bi。 2<=n<=1000000 100<=wi<=6500 1<=ai,bi<=n ai各不相等,bi各不相等 (ai)<>(bi) 样例中依次交换数字(2,5)(3,4)(1,5)
Output
一个数,最小代价。
Sample Input
6
2400 2000 1200 2400 1600 4000
1 4 5 3 6 2
5 3 2 4 6 1
Sample Output
11200
HINT
感谢MT大牛贡献译文.
Source
有两个步骤:
1.合并两个环!!
2.找到环上的最小值然后消掉
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 1000010
using namespace std;
inline void read(int &num){
char ch = getchar();num = 0;
for(; ch < '!'; ch = getchar());
int flag = 1;
if(ch == '-'){flag = -1;ch = getchar();}
for(; ch > '!'; ch = getchar())
num = num * 10 + ch - 48;
num *= flag;
}
int w[maxn];
int n;
int nxt[maxn], a[maxn];
int tim;
int vis[maxn];
long long ans;
int que[maxn];
struct Circle{
int size, minw, type, pos;
long long sum;
bool flag;
bool operator<(const Circle& k)const{
if(minw != k.minw)return minw < k.minw;
return size < k.size;
}
}c[maxn];
void BFS(int S){
int head = 0, tail = 0, p = S;
long long ret = 0;
que[tail ++] = S;
int size = 0, minw = 0x7fffffff;
while(head != tail){
int u = que[head ++];
vis[u] = tim;
if(vis[nxt[u]] == 0)
que[tail ++] = nxt[u];
size ++;
ret += w[u];
if(w[u] < minw){
minw = w[u];
p = u;
}
}
c[tim].size = size;
c[tim].minw = minw;
c[tim].sum = ret;
c[tim].type = tim;
c[tim].pos = p;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("slo.in", "r", stdin);
freopen("slo.out", "w", stdout);
#endif
read(n);
for(int i = 1; i <= n; i ++)
read(w[i]);
int x;
for(int i = 1; i <= n; i ++)
read(a[i]);
for(int i = 1; i <= n; i ++){
read(x);
nxt[a[i]] = x;
}
for(int i = 1; i <= n; i ++)
if(!vis[i]){tim ++;BFS(i);}
sort(c + 1, c + 1 + tim);
for(int i = 2; i <= tim; i ++){
if(1ll * c[i].minw * (c[i].size - 2) > 1ll * c[1].minw * c[i].size + c[i].minw + c[1].minw){
c[i].flag = true;
c[1].size += c[i].size;
c[1].sum += c[i].sum;
ans += c[i].minw + c[1].minw;
}
}
for(int i = 1; i <= tim; i ++){
if(c[i].flag)continue;
ans += c[i].sum + 1ll * c[i].minw * (c[i].size - 2);
}
printf("%lld\n", ans);
return 0;
}