【题解】LuoGu3482:[POI2009]SLO-Elephants

原题传送门
首先肯定要找环,枚举终点,往前找,因为这样保证前路只有一条
找出环后,看看代价怎么算
设找出的环总和 s u m sum sum,环中元素个数 l e n len len,环中最小元素 M i n Min Min,整个输入中最小元素 m i n w minw minw

  • l e n = 1 len=1 len=1,整个环就一个元素,自己指向自己,什么都不用搞
  • l e n − 2 len-2 len2,两个元素互相指向对方,交换一下就好了,代价为 s u m sum sum
  • l e n > 2 len>2 len>2,在草稿纸上模拟一下, 发现可以理解成每个元素交换一次到自己的目标位置,途中有一个元素作为一个中转总共帮另外 l e n − 1 len-1 len1个元素交换,同时把自己也交换好了。那个元素自然是取最小的,即 M i n Min Min,它的次数呢,总共是 l e n − 1 len-1 len1次,也可以理解为在所有元素都交换一次的情况下,它多交换了 l e n − 2 len-2 len2次。所以代价为 s u m + ( l e n − 2 ) M i n sum+(len-2)Min sum+(len2)Min
    但是还有一种方案,我们可以把这个环外的一个元素拿进来作为中转,所以这个外来者一定得满足整个输入最小,即 m i n w minw minw,然后计算代价,这里不再赘述,为 s u m + M i n + ( l e n + 1 ) m i n w sum+Min+(len+1)minw sum+Min+(len+1)minw

Code:

#include <bits/stdc++.h>
#define maxn 1000010
#define LL long long
using namespace std;
LL w[maxn], ans, sum, len, Min, minw;
int a[maxn], b[maxn], pre[maxn], vis[maxn], n;

inline int read(){
    
    
	int s = 0, w = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
	for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
	return s * w;
}

void dfs(int u){
    
    
	vis[u] = 1, sum += w[a[u]], ++len, Min = min(Min, w[a[u]]);
	if (!vis[pre[b[u]]]) dfs(pre[b[u]]);
}

int main(){
    
    
	n = read();
	minw = 1e9;
	for (int i = 1; i <= n; ++i) minw = min(minw, w[i] = read());
	for (int i = 1; i <= n; ++i) pre[a[i] = read()] = i;
	for (int i = 1; i <= n; ++i) b[i] = read();
	for (int i = 1; i <= n; ++i)
		if (!vis[i]){
    
    
			sum = 0, len = 0, Min = 1e9;
			dfs(i);
			if (len == 1);
			else if (len == 2) ans += sum;
			else ans += sum + min((len - 2) * Min, (len + 1) * minw + Min);
		}
	printf("%lld\n", ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/ModestCoder_/article/details/108555269