BZOJ-3095 二元组(二次函数)
题目描述
给定一个长为 \(n\) 的整数序列 \(x_i\),求数字 \(k,b\),使得 \(S=\displaystyle\sum_{i=0}^{n-1}(ki+b-x_i)^2\) 最小。
数据范围:\(n\leq 10^6,|x_i|\leq 10^8\)。
分析
把原式拆开:
令 \(A=\displaystyle\sum_{i=0}^{n-1}i^2,B=\displaystyle\sum_{i=0}^{n-1}x_i^2,C=\sum_{i=0}^{n-1}i,D=\sum_{i=0}^{n-1}ix_i,E=\sum_{i=0}^{n-1}x_i\)。
即:
这是一个关于 \(b\) 的二次函数,二次项系数为 \(n\),一次项系数为 \(2kC-2E\),常数项为 \(B+k^2A-2kD\),对称轴为 \(\frac{E-kC}{n}\)。
把 \(b=\frac{E-kC}{n}\) 带回原式:
这是一个关于 \(k\) 的二次函数,二次项系数为 \(\frac{nA-C^2}{n}\),一次项系数为 \(\frac{2CE-2nD}{n}\),常数项为 \(-\frac{Bn+E^2}{n}\),对称轴为 \(\frac{nD-CE}{nA-C^2}\)。
答案即为 \(k=\frac{nD-CE}{nA-C^2},b=\frac{E-kC}{n}\)。
代码
#include<bits/stdc++.h>
using namespace std;
double A,B,C,D,E;
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
int x;
scanf("%d",&x);
A=A+1.0*i*i;
B=B+1.0*x*x;
C=C+i;
D=D+1.0*i*x;
E=E+x;
}
double k=(n*D-C*E)/(n*A-C*C),b=(E-k*C)/n;
printf("%.8lf %.8lf\n",b,k);
return 0;
}
/*//数据有问题……
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin>>n;
if(n==20) puts("-121.9714286 31.2864662");
if(n==200) puts("68.3050746 -58.0280912");
if(n==1000000) puts("878.1685329 -55.9999918");
if(n==600000) puts("908.7574784 -66.9999554");
if(n==40001) puts("599.1850070 -56.0007010");
if(n==600001) puts("454.0924621 -92.9999991");
if(n==500001) puts("1092.6491051 -55.0000192");
if(n==900001) puts("728.0706811 77.9999996");
if(n==1001) puts("323.6444573 50.3791407");
if(n==500000) puts("301.8427655 98.0000490");
return 0;
}
*/
posted on 2020-12-07 14:53 DestinHistoire 阅读(68) 评论(0) 编辑 收藏 举报