CF gym 102483(NWERC 2018) A题 解答

分析

题目大意:给出 n 个点的坐标 (xi,yi) ,其中 i[1,n] ,要求构造相应的点 (pi,qi)p,q 满足 pi1pi, qi1qi,使得 ((xipi)2+(yiqi)2) 最小。

注意到 x,yp,q 都是分别独立的,因此只需求 (xipi)2 最小值即可。

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

其中合并方法为: 将 scur 加上 slastccur 加上 clast ,删去 last

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

简便起见,分别将 cur,last 结点记为 ei+1,eiiei 有属性 si,cisi/ci 记为 vi

ei 对应的区间为 [L,k]ei+1 对应的区间为 [k+1,R]

先给出引理:对于 i=1n(aix)2n ,当且仅当 x=a¯ 时取到最小值。

证明:展开,可知 i=1n(aix)2n 是关于 x 的二次函数,由展开式可知,其在且只在 x=a¯ 取到最小值。

  • vi+1vi 时,只需证明不合并合并的花费小即可。

v=(si+si+1)/(ci+ci+1)

由所给的做法,对于相应的 p 可知在 [L,k]p=vi ,而在 [k+1,R]p=vi+1 ,这样的 p 值是合法的。

只需证明 j=LR(xjv)2u=Lk(xuvi)2+w=k+1R(xwvi+1)2

由引理,

u=Lk(xuv)2u=Lk(xuvi)2

w=k+1R(xwv)2w=k+1R(xwvi+1)2

将上两式相加即证。

  • vi+1<vi 时,只需证明合并不合并的花费小即可。

由所给的做法,对于相应的 p ,记在 [L,k]p=pf ,而在 [k+1,R]p=ps ,应有 pspf

只需证明 j=LR(xjv)2u=Lk(xupf)2+w=k+1R(xwps)2

因为 w=k+1R(xwps)2u=Lk(xupf)2

u=Lk(xupf)2+w=k+1R(xwps)2u=Lk(xupf)2+w=k+1R(xwpf)2=j=LR(xjpf)2

由引理,显然有 j=LR(xjpf)2j=LR(xjv)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 @   HinanawiTenshi  阅读(102)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示