题意:给出一个长为n的序列a,每次可以交换两个元素,代价为其之和,求最小总代价使得序列变为升序。
这几天怎么净看见神题了...
这题考群论思想...
考虑一个循环节,有两种把它归位的方式,一种是用循环内的最小值去挪移每个值,另一种是用全局最小值去挪移每个值
思路出来以后就ok了,真是一道神题啊qaq...
哦对要离散化
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<cstdlib> #include<ctime> using namespace std; inline int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { (ans *= 10) += ch - '0'; ch = getchar(); } return ans * op; } typedef int mainint; #define int long long const int inf = 0x7f7f7f7f; const int maxn = 2e5 + 5; int a[maxn],tp[maxn]; int to[maxn]; int minm = inf; bool vis[maxn]; int ans; mainint main() { int n = read(); for(int i = 1;i <= n;i++) a[i] = read(),tp[i] = a[i],minm = min(minm,a[i]); sort(tp + 1,tp + 1 + n); for(int i = 1;i <= n;i++) to[i] = lower_bound(tp + 1,tp + 1 + n,a[i]) - tp; for(int i = 1;i <= n;i++) { if(vis[i]) continue; int m1 = inf,u = i,sz = 0,sum = 0; while(!vis[u] && u != to[u]) { vis[u] = 1; m1 = min(a[u],m1); sum += a[u]; sz++; u = to[u]; } if(!sz) continue; ans += min(m1 * (sz - 1) + sum - m1,minm + m1 + minm * (sz - 1) + sum - m1 + m1 + minm); } printf("%lld",ans); }