国庆七连测(六) 战斗

34 篇文章 0 订阅
13 篇文章 0 订阅

【简化题意】
一条长度为n的序列中选取k个数,并选取其中一个数作为基准数。其他k-1个数减去基准数后的平方和等于代价。求最小代价是多少。n<=1e5
输入时的序列不严格单调递增。
【分析】
首先我们改变一下思路,每次选取一个基准数,然后选择剩下的k-1个数。显然的这k-1个数一定是连续的,且分布在基准数的左右两边。并且随着基准数的位置右移单调递增。
利用two-pointer的思想我们用O(n)总时间确定每个基准数对应的最小区间,均摊上是O(1)。问题是如何快速区间内差值平方和。

设 x i 为 上 一 个 区 间 中 编 号 为 i 的 元 素 和 基 准 值 的 差 值 。 设x_i为上一个区间中编号为i的元素和基准值的差值。 xii
那 么 随 着 基 准 数 位 置 的 右 移 , 差 值 会 增 长 或 减 少 。 基 准 数 左 边 会 增 加 Δ , 右 边 会 减 小 Δ 。 那么随着基准数位置的右移,差值会增长或减少。基准数左边会增加 \Delta ,右边会减小\Delta。 ΔΔ
从 原 来 的 x i 2 , 变 为 ( x i + Δ ) 2 , 展 开 后 得 到 x i 2 + 2 Δ x i + Δ 2 从原来的x_i^2,变为{(x_i+\Delta)}^2,展开后得到x_i^2+2\Delta x_i+\Delta^2 xi2(xi+Δ)2,xi2+2Δxi+Δ2
Δ 和 Δ 2 可 以 O ( 1 ) 计 算 , 预 先 维 护 好 Σ x i 我 们 也 可 以 每 次 O ( 1 ) 求 解 区 间 ( 虽 然 好 像 常 数 极 大 ) \Delta 和\Delta^2可以O(1)计算,预先维护好\Sigma x_i我们也可以每次O(1)求解区间(虽然好像常数极大) ΔΔ2O(1)ΣxiO(1)()
更优的O(n)算法请关注liuzibujian的blog。
Code:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL maxn=1e5+1000;
LL pos,l,r,n,k,base;
LL suml=0,sumr=0,sum=0;
LL a[maxn],ans;
inline void read(LL &x){
	x=0;char tmp=getchar();
	while(tmp<'0'||tmp>'9') tmp=getchar();
	while(tmp>='0'&&tmp<='9') x=(x<<1)+(x<<3)+tmp-'0',tmp=getchar();
}
inline LL pw(LL x){
	return x*x;
}
int main(){
	freopen("battle.in","r",stdin);
	freopen("battle.out","w",stdout);
	cin>>n>>k;
	for(int i=1;i<=n;i++)
		read(a[i]);
	l=1,pos=1,r=k,base=a[1],suml=0;
	for(int i=2;i<=k;i++)
		sumr+=a[i]-base,sum+=pw(a[i]-base);
	ans=sum;
	while(pos<n){
		LL d=a[pos+1]-a[pos];
		sum=sum+2*d*suml+(pos-l+1)*d*d-2*d*sumr+(r-pos)*d*d;
		suml+=(pos-l+1)*d,sumr-=(r-pos)*d;
		pos++; base=a[pos];
		while(pw(a[l]-base)>pw(a[r+1]-base)) sum=sum-pw(a[l]-base)+pw(a[r+1]-base),suml-=base-a[l],sumr+=a[r+1]-base,l++,r++;
		ans=min(ans,sum);
	}
	printf("%lld\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值