P4966 题解
link
先把这个毒瘤题面翻译一下(汉译汉是吧)。
有
运算方式为无符号整型运算。
求对于所有的权值排列生成的图,图的代价最少是多少,下面定义“图的代价”:
设
考虑怎么做。直接枚举排列显然不可行,然而边权排列和图的形态之间的联系就靠着上面
想到了模拟退火之后,我们接下来要考虑的问题就是如何快速求解一张图的代价。
首先考虑一个性质:
直接爆搜是
第一发没过,调高了降温系数就过掉了。加了卡时,并且喜提最劣解。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=210;
#define ll long long
#define ui unsigned int
#define db double
ui ksm(ui x,ui y){
ui res=1;
while(y){
if(y&1)
res=res*x;
x=x*x;
y/=2;
}
return res;
}
int n,m;
ui q;
struct dat{
int x;
int y;
int z;
}g1[N],g2[N],g3[N];
int v[N],uf[N];
ll sz[N],val[N];
ll ans=1000000000000000;
int cmp(dat x,dat y){
return x.z<y.z;
}
int find(int x){
if(uf[x]==x)
return x;
return uf[x]=find(uf[x]);
}
int rec[N];
void gen(){
for(int i=1;i<=m;i++){
ui x=((ksm(q,(ui)i)+((ui)i*v[i]))%n+n)%n+1;
ui y=((ksm(q,(ui)i)-((ui)i*v[i]))%n+n)%n+1;
g1[i]={x,y,v[i]};
}
}
ll calc(){
gen();
ll res=0;
for(int i=1;i<=m;i++)
g2[i]=g1[i];
sort(g2+1,g2+m+1,cmp);
for(int i=1;i<=n;i++)
uf[i]=i;
int tot=0;
for(int i=1;i<=m;i++){
int x=g2[i].x;
int y=g2[i].y;
int z=g2[i].z;
int f1=find(x);
int f2=find(y);
if(f1==f2)
continue;
uf[f2]=f1;
g3[++tot]=g2[i];
}
for(int i=1;i<=n;i++){
val[i]=0;
uf[i]=i;
sz[i]=1;
}
for(int i=1;i<=tot;i++){
int x=g3[i].x;
int y=g3[i].y;
int z=g3[i].z;
int f1=find(x);
int f2=find(y);
val[f1]=max(val[f1]+z*sz[f2],val[f2]+z*sz[f1]);
sz[f1]+=sz[f2];
uf[f2]=f1;
res=max(res,val[f1]);
}
if(res<ans){
ans=res;
for(int i=1;i<=m;i++)
rec[i]=g1[i].z;
}
return res;
}
void simulate_anneal(){
db T=1e5;
db low=0.999;
while(T>=1e-4){
int X=rand()%m+1;
int Y=rand()%m+1;
ll nw=calc();
swap(v[X],v[Y]);
ll res=calc();
if(res>nw){
db del=res-nw;
if((db)rand()/RAND_MAX>exp(-del/T))
swap(v[X],v[Y]);
}
T*=low;
}
}
int main(){
srand(time(0));
cin>>n>>m>>q;
for(int i=1;i<=m;i++)
cin>>v[i];
while(1.0*clock()/CLOCKS_PER_SEC<=1.8)
simulate_anneal();
cout<<ans<<'\n';
for(int i=1;i<=m;i++)
cout<<rec[i]<<'\n';
return 0;
}