题解

圆上有$n$个关键点,最小化他们到同一点的距离和。

考虑在圆上顺时针移动所求点,在移动的过程中一些关键点到该点距离增加,一些关键点到该点距离减少,一些关键点距离先增加再减少或是先减少再增加。考虑以在移动过程中每个关键点 增加/减少 的分界处(显然这些分界处即为每个关键点在圆上相对的点和自己本身)为单位枚举移动所求点,那么在移动过程中关键点到该点的距离要么增加,要么减少,只需要维护这两种情况分别有多少个点即可。(显然所求点一定在分界处上,因为在分界处之间的距离和变化单调,即分界处之间的距离和一定小于两边某一个分界处的距离和)

把$n$个关键点移动到圆上$n$等分位置也可以用相同的方法,先将点按极角排序,默认第$i$个点移动到第$i$个$n$等分位置即可(即$a_i-=360i/n$,在此次操作后第$i$个等分位置移动到了第$0$个等分位置,$a_i$移动到了$a_i-360i/n$)

正确的做法是将第$i$个关键点跟第$i$个等分点绑定,然后考虑移动一起所有等分点的过程。

以下是把$n$个关键点移动到圆上$n$等分位置,最小化距离和的算法。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+5;

int n;
ll a[N],b[N],q[N*2];
multiset<pair<ll,int>> s;

int main(){
cin>>n;
ll r=36000000ll*n;
ll dec=100000ll*n;
for(int i=1;i<=n;++i){
double b;
cin>>b;
a[i]=b*dec+0.6;
}
sort(a+1,a+n+1);
ll tot=0;
for(int i=1;i<=n;++i){
ll s=(a[i]-36000000ll*(i-1)+r)%r;
b[i]=s;
}
sort(b+1,b+n+1);
for(int i=n;i>=1;--i)b[i]=b[i]-b[1];
for(int i=1;i<=n;++i)tot+=min(b[i],r-b[i]);
ll cnt1=0,cnt2=0;
ll ans=tot;
for(int i=1;i<=n;++i){
ll x1=b[i];
ll x2=(x1+r/2)%r;
if(x1<r/2){
cnt1++;
s.insert({x1,1});
s.insert({x2,-1});
}
else{
cnt2++;
s.insert({x1,1});
s.insert({x2,-1});
}
q[++q[0]]=x1;
q[++q[0]]=x2;
}
sort(q+1,q+q[0]+1);
q[++q[0]]=r;
ll last=0;
for(auto p:s){
tot-=cnt1*(p.first-last);
tot+=cnt2*(p.first-last);
last=p.first;
ans=min(ans,tot);
if(p.second==1)cnt1--,cnt2++;
else cnt1++,cnt2--;
}
double res=1.*ans/dec;
res=res/180*acos(-1);
printf("%.10lf\n",res);
}