卧薪尝胆,厚积薄发。
POI2009 SLO-Elephants
Date: Tue Oct 30 21:20:06 CST 2018
In Category:
NoCategory
Description:
对于一个
$1\sim N$
的排列
$a_i$
,每次你可以交换两个数
$a_x$
与
$a_y(x\ne y)$
,代价为
$W(a_x)+W(a_y) $
若干次交换的代价为每次交换的代价之和。请问将
$a_i$
变为
$b_i$
所需的最小代价是多少。
$1\leqslant n\leqslant 10^6$
Solution:
把每个向他在另一个序列的位置连边,每个点入度为一出度为一一定是很多环,如果环大小为
$1$
,那就不用管,如果大小为
$2$
,直接交换,如果大小
$\geqslant 3$
,那就有两种方式,一是拿一个环中的最小的一个一个换下去,二是从外面拿一个最小的充当媒介,分别计算一下代价取
$\min$
累加即可。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<vector>
#include<cctype>
#include<cstring>
using namespace std;
int n;
#define MAXN 1000010
int w[MAXN];
int a[MAXN],b[MAXN];
int fr[MAXN];
struct edge
{
int to,nxt,w;
}e[MAXN];
int edgenum = 0;
int lin[MAXN] = {0};
void add(int a,int b,int w)
{
e[++edgenum] = (edge){b,lin[a],w};lin[a] = edgenum;
return;
}
bool v[MAXN];
int tot = 0;
vector<int> c[MAXN];
void get(int k)
{
v[k] = true;
for(int i = lin[k];i != 0;i = e[i].nxt)
{
c[tot].push_back(e[i].w);
if(!v[e[i].to])get(e[i].to);
}
return;
}
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;++i)scanf("%d",&w[i]);
for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
for(int i = 1;i <= n;++i)scanf("%d",&b[i]);
for(int i = 1;i <= n;++i)fr[a[i]] = i;
for(int i = 1;i <= n;++i)add(fr[b[i]],i,w[a[i]]);
for(int i = 1;i <= n;++i)
{
if(!v[i])
{
++tot;
get(i);
}
}
int minn = 0x3f3f3f3f;
for(int i = 1;i <= n;++i)minn = min(minn,w[i]);
long long ans = 0;
for(int i = 1;i <= tot;++i)
{
if(c[i].size() == 1)continue;
if(c[i].size() == 2)
{
ans += c[i][0] + c[i][1];
continue;
}
long long val1 = 0,val2 = 0;
int minv = 0x3f3f3f3f;
for(int j = 0;j < c[i].size();++j)
{
minv = min(minv,c[i][j]);
val1 += c[i][j];
}
val1 += (long long)minv * (c[i].size() - 2);
for(int j = 0;j < c[i].size();++j)
{
val2 += c[i][j];
}
val2 += minv + (long long)minn * (c[i].size() + 1);
ans += min(val1,val2);
}
cout << ans << endl;
return 0;
}
In tag:
玄学
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡