AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=1697
【题解】
将所给值离散之后,就是一个置换群问题。
初始状态为离散值,目标状态为1~n,交换这一行为映射了置换,我们把这一置换循环分解而得到一个置换群。
对于群内的每个环,有一个很贪心的办法就是每次都让权值最小的那个元素去和其他的元素交换,
这样交换的代价为sum-min+(len-1)*min
其中sum为环内所有元素的权重之和,min为环内最小的元素,len为这个环的循环节。
经过len-1次交换,我们让这个环变的有序了。
不过这里还有一个策略,引进外援,就是从全局中找一个最小的small,将其引入到这个环中,参与置换。
举例(1) (8 6 9 7) 我们可以先把1和6交换,让1去参与环的置换,结束后再将6交换进来。
如此的代价为sum+min+(len+1)*small 取这两种策略里代价小的那种即可。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
using namespace std;
#define FILE "read"
#define MAXN 100010
#define INF 1000000000
#define up(i,j,n) for(int i=j;i<=n;++i)
#define dn(i,j,n) for(int i=j;i>=n;--i)
namespace INIT{
char buf[1<<15],*fs,*ft;
inline char getc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
inline int read(){
int x=0,f=1; char ch=getc();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getc();}
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getc();}
return x*f;
}
}using namespace INIT;
int n,ans,len,Minn,a[MAXN],b[MAXN],v[MAXN],vis[MAXN],size[MAXN],sum[MAXN],minn[MAXN];
int find(int x){
int l=1,r=n,temp;
while(l<=r){
int mid=(l+r)>>1;
if(v[mid]>=x) {temp=mid; r=mid-1;}
else l=mid+1;
}
return temp;
}
void solve(int x){
vis[x]=1; size[++len]=1; sum[len]+=a[x]; minn[len]=min(minn[len],a[x]);
for(int i=b[x];a[i]!=a[x];i=b[i]){
vis[i]=1; size[len]++; sum[len]+=a[i]; minn[len]=min(minn[len],a[i]);
}
}
int main(){
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
n=read(); memset(minn,127/3,sizeof(minn)); Minn=INF;
up(i,1,n) a[i]=read(),v[i]=a[i],Minn=min(Minn,a[i]);
sort(v+1,v+n+1);
up(i,1,n) b[i]=find(a[i]);
up(i,1,n) if(i!=b[i]&&!vis[i]) solve(i);
up(i,1,len){
ans+=min(sum[i]+minn[i]*(size[i]-2),sum[i]+minn[i]+(size[i]+1)*Minn);
}
printf("%d\n",ans);
return 0;
}