数轴上有 (n) 种点,总共有 (m) 个,每个点有它的坐标 (x_i) 和种类 (p_i)。求一个点,使得所有种类点中与这个点的最小距离的平方和最小。(n le 10^4, m le 10^5, x_i le 10^5)
Solution
要最小化 (sum_{i=1}^n (x-x_i)^2),显然在选定了每个种类使用的点以后,这是一个关于 (x) 的二次函数,其最小值为
[sum_{i=1}^n x_i^2 - frac 1 n sum_{i=1}^n x_i
]
于是现在我们只需要考虑如何选择每个种类使用的点
对于同一种点按坐标从小到大排序,每次枚举把某种点替换成他的下一个
由于原式取得最小值的条件是 (x= frac 1 n sum_{i=1}^n x_i)
我们将每次替换用一个二元组 ((x_i,x_i')) 表示,那么我们只需要将所有二元组按照 (x_i+x_i') 排序,就一定不会错过最优解
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 100005;
int n,m,t1,t2,t3;
vector <int> g[N];
struct pii {
int x,y;
};
vector <pii> p;
double s,s2,ans=1e18,tans=0;
signed main() {
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=m;i++) {
cin>>t1>>t2;
g[t2].push_back(t1);
}
for(int i=1;i<=n;i++) {
sort(g[i].begin(),g[i].end());
}
for(int i=1;i<=n;i++) {
if(g[i].size()) {
s+=g[i][0];
s2+=g[i][0]*g[i][0];
}
for(int j=1;j<g[i].size();j++) {
p.push_back({g[i][j-1],g[i][j]});
}
}
sort(p.begin(),p.end(),[](pii a,pii b)->bool{return a.x+a.y<b.x+b.y;});
ans=min(ans,s2-s*s/n);
tans=s/n;
for(int i=0;i<p.size();i++) {
s-=p[i].x;
s2-=p[i].x*p[i].x;
s+=p[i].y;
s2+=p[i].y*p[i].y;
ans=min(ans,s2-s*s/n);
if(fabs(s2-s*s/n-ans)<1e-6) tans=s/n;
}
printf("%.4lf",tans);
}