数轴上有 \(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);
}
原文地址:https://www.cnblogs.com/mollnn/p/12655394.html
时间: 2024-11-08 23:09:11