友好的序列

23 篇文章 0 订阅
18 篇文章 0 订阅
描述

在信息竞赛班的一次欢乐活动中,为了增强友谊,同学们站成了一列,编号从 1 1 1 n n n。每个人手上都有一个球,球上有一个数字。游戏规定对于任意两个人 ( i , j ) (i,j) (i,j),他们的友好度为 ( i − j ) 2 + g ( i , j ) 2 (i−j)^2+g(i,j)^2 (ij)2+g(i,j)2其中 g ( i , j ) = ∑ j k = i + 1 a k g(i,j)=∑jk=i+1ak g(i,j)=jk=i+1ak.
请你帮助老师计算出任意两个人的最小可能友好度是多少?

输入

第一行 1 1 1个整数 N N N
接下来一行是 N N N个整数,表示每个人手上球上的数字。

输出

输出 1 1 1个整数表示最小的友好度

样例输入
4
1 0 0 -1
样例输出
1
提示

40 40 40%的数据保证: 1 &lt; = N &lt; = 1000 , − 1000 &lt; = a i &lt; = 103 1&lt;=N&lt;=1000,−1000&lt;=ai&lt;=103 1<=N<=1000,1000<=ai<=103
100 100 100%的数据保证: 1 &lt; = N &lt; = 105 , − 105 &lt; = a i &lt; = 105 1&lt;=N&lt;=105,−105&lt;=ai&lt;=105 1<=N<=105,105<=ai<=105

解析:

首先得想到的是暴力30分的程序,暴搜加前缀和
然后,我们需要反应过来:目标是求 ( i − j ) 2 + ( p r e [ i ] − p r e [ j ] ) 2 (i-j)^2+(pre[i]-pre[j])^2 (ij)2+(pre[i]pre[j])2,可以看做求所有点中点 ( i , p r e [ i ] ) (i,pre[i]) (i,pre[i])到点 ( j , p r e [ j ] ) (j,pre[j]) (j,pre[j])的最小距离( i ! = j i!=j i!=j
接下来就该想怎么优化求正解了,先是一堆的剪枝:

  • 用分治求出初始解(不需要准确,只是个模糊范围)
  • 然后枚举每个点计算x轴方向与中间点的距离差,小于初始解算术平方根的点加入队列中
  • 接下来将队列中的点按y的大小从小到大排序,枚举每两个点组成的点对,将在y轴方向上距离小于解的算术平方根的点对都计算一遍新解,在其与原来的解中取最优解;
CODE
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-f;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=(s<<3)+(s<<1)+ch-'0';
		ch=getchar();
	}
	return s*f;
}
const int N=1e5+5;
struct fjy{
	int x,y;
}p[N],q[N];
int n,x;
int pre[N];
int min(int x,int y){
	return x<y?x:y;
}
int dist(const fjy &a,const fjy &b){//家计算点a与b的距离的平方 
	return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
bool comy(const fjy &y,const fjy &t){
	return y.y<t.y;
}
int work(int l,int r){
	if(l==r) return 9e18;
	if(r-l==1) return dist(p[l],p[r]);
	if(r-l==2) return min(min(dist(p[l],p[r]),dist(p[l+1],p[r])),dist(p[l],p[l+1]));
	int mid=(l+r)>>1,cnt=0;
	int ret=min(work(l,mid),work(mid+1,r));
	for(int i=l;i<=r;i++) if(abs(p[i].x-p[mid].x)<sqrt(ret)) q[++cnt]=p[i];//渝比较x1-xmid是否大于ret,若小于则加入队列进行其他操作,用于优化 
	sort(q+1,q+cnt+1,comy);
	for(int i=1;i<cnt-1;i++)
	for(int j=i+1;j<=cnt;j++){
		if((q[j].y-q[i].y)*(q[j].y-q[i].y)>ret) break;
		ret=min(ret,dist(q[i],q[j]));
	}
	return ret;
}
signed main(){
	n=read();
	for(int i=1;i<=n;i++){
		x=read();
		pre[i]=pre[i-1]+x;
		p[i].x=i,p[i].y=pre[i];
	}
	printf("%lld",work(1,n));
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值