描述
在信息竞赛班的一次欢乐活动中,为了增强友谊,同学们站成了一列,编号从
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
(i−j)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
<
=
N
<
=
1000
,
−
1000
<
=
a
i
<
=
103
1<=N<=1000,−1000<=ai<=103
1<=N<=1000,−1000<=ai<=103
100
100
100%的数据保证:
1
<
=
N
<
=
105
,
−
105
<
=
a
i
<
=
105
1<=N<=105,−105<=ai<=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
(i−j)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;
}