CF gym 102483(NWERC 2018) A题 解答

分析

题目大意:给出 \(n\) 个点的坐标 \((x_i, y_i)\) ,其中 \(i\in [1,n]\) ,要求构造相应的点 \((p_i,q_i)\)\(p,q\) 满足 \(p_{i-1}\leq p_{i},~q_{i-1}\leq q_{i}\),使得 \(\sum ((x_i-p_i)^2+(y_i-q_i)^2)\) 最小。

注意到 \(x,y\)\(p,q\) 都是分别独立的,因此只需求 \(\sum (x_i-p_i)^2\) 最小值即可。

先给出具体做法:对于每个 \(x_k\) ,都创建一个有值和 \(sum\) (简记为 \(s\)),\(cnt\) (简记为 \(c\))两个属性的结点,当当前结点 \(cur\)先前结点满足: \(s_{cur}/c_{cur} < s_{last}/c_{last}\) 时,就将 \(cur\)\(last\) 进行合并得到更新后的 \(cur\) ,如此下去直到最后的 \(cur\)\(last\) 满足: \(s_{cur}/c_{cur} \geq s_{last}/c_{last}\)

其中合并方法为: 将 \(s_{cur}\) 加上 \(s_{last}\)\(c_{cur}\) 加上 \(c_{last}\) ,删去 \(last\)

下面说明这样做即可得到最优解:

简便起见,分别将 \(cur,last\) 结点记为 \(e_{i+1},e_i\)\(\forall i\)\(e_i\) 有属性 \(s_i,c_i\)\(s_i/c_i\) 记为 \(v_i\)

\(e_i\) 对应的区间为 \([L,k]\)\(e_{i+1}\) 对应的区间为 \([k+1,R]\)

先给出引理:对于 \(\frac{\sum_{i=1}^n (a_i-x)^2}{n}\) ,当且仅当 \(x=\overline a\) 时取到最小值。

证明:展开,可知 \(\frac{\sum_{i=1}^n (a_i-x)^2}{n}\) 是关于 \(x\) 的二次函数,由展开式可知,其在且只在 \(x=\overline a\) 取到最小值。

  • \(v_{i+1} \geq v_i\) 时,只需证明不合并合并的花费小即可。

\(v=(s_i+s_{i+1})/(c_i+c_{i+1})\)

由所给的做法,对于相应的 \(p值\) 可知在 \([L,k]\)\(p=v_i\) ,而在 \([k+1,R]\)\(p=v_{i+1}\) ,这样的 \(p\) 值是合法的。

只需证明 \(\sum_{j=L}^R (x_j-v)^2 \geq \sum_{u=L}^k(x_u-v_i)^2+\sum_{w=k+1}^R (x_w-v_{i+1})^2\)

由引理,

\(\sum_{u=L}^k(x_u-v)^2\geq \sum_{u=L}^k(x_u-v_i)^2\)

\(\sum_{w=k+1}^R (x_w-v)^2 \geq \sum_{w=k+1}^R (x_w-v_{i+1})^2\)

将上两式相加即证。

  • \(v_{i+1} < v_i\) 时,只需证明合并不合并的花费小即可。

由所给的做法,对于相应的 \(p值\) ,记在 \([L,k]\)\(p=p_f\) ,而在 \([k+1,R]\)\(p=p_s\) ,应有 \(p_s\geq p_f\)

只需证明 \(\sum_{j=L}^R (x_j-v)^2 \leq \sum_{u=L}^k(x_u-p_f)^2+\sum_{w=k+1}^R (x_w-p_s)^2\)

因为 \(\sum_{w=k+1}^R (x_w-p_s)^2 \geq \sum_{u=L}^k(x_u-p_f)^2\)

\(\sum_{u=L}^k(x_u-p_f)^2+\sum_{w=k+1}^R (x_w-p_s)^2 \geq \sum_{u=L}^k(x_u-p_f)^2+\sum_{w=k+1}^R (x_w-p_f)^2 = \sum_{j=L}^R (x_j-p_f)^2\)

由引理,显然有 \(\sum_{j=L}^R (x_j-p_f)^2 \geq \sum_{j=L}^R (x_j-v)^2\)

至此证毕。

所以对于所给的做法,每一次操作都是最优的做法,因此整个做法是最优的。

代码:

#pragma GCC optimize("O3")
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define debug(x) cerr << #x << ": " << x << endl
#define pb(a) push_back(a)
#define set0(a) memset(a,0,sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define ceil(a,b) (a+(b-1))/b
#define INF 0x3f3f3f3f
#define ll_INF 0x7f7f7f7f7f7f7f7f
typedef long long ll;
typedef pair<int,int> PII;
typedef pair<double,double> PDD;

inline int read()
{
	int x=0,y=1;char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();}
	while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
	return x*y;
}

const int N=1e5+5;
const double eps=1e-12;
int n;
double x[N], y[N];

double stk_sum[N];
int stk_cnt[N];
int top;

double cal(double a[]){
	set0(stk_sum), set0(stk_cnt);
	top=0;
	
	rep(i, 1, n){
		double cur_sum=a[i]; int cur_cnt=1;
		while(top && cur_sum*stk_cnt[top]+eps<stk_sum[top]*cur_cnt){
			cur_sum+=stk_sum[top];
			cur_cnt+=stk_cnt[top--];
		}
		stk_sum[++top]=cur_sum, stk_cnt[top]=cur_cnt;
	}		
	
	int id=0;
	double res=0;
	rep(i, 1, top) rep(j, 1, stk_cnt[i]){
		id++;
		double avg=stk_sum[i]/stk_cnt[i];
		res+=(avg-a[id])*(avg-a[id]);
	}
	return res;
}

int main(){
	cin>>n;
	rep(i, 1, n) cin>>x[i]>>y[i];
	
	double res=cal(x)+cal(y);
	printf("%.10lf\n", res);	
	
    return 0;
}
posted @ 2021-05-11 20:19  HinanawiTenshi  阅读(94)  评论(0编辑  收藏  举报