关于H,提供一个好像没什么人想的思路
因为题目要求的公式是形如:
for (int i=1;i<=n;i++) for (int j=i;j<=n;j++)
的形式。(不妨叫他公式1)
一开始我想到排序+前缀和处理,因为我把这个公式看成了:
for (int i=1;i<=n;i++) for (int j=1;j<=n;j++)
这样的形式。(不妨叫他公式2)
后来想了半天没想到题解里说的枚举1到1000,就回来想是不是能顺着一开始的思路做下去,结果真的能,先画一张图来理解一下一个手车的样例:
4
1 2 3 4
先不管答案是什么,先看图
显然,对于从左到右的每一个数,对于原始的的公式1,只有画出来的对角线的右上侧被计算了,如果是公式2,那么被计算的部分就是全部的整个矩阵(讲的有点模糊,希望你能听懂)
所以得出结论:公式1的结果=(公式2的结果+对角线的结果)/2
这样对公式1的求解就转化为了对公式2的求解
接下来思考公式2中打开绝对值的问题
显然对于当前的ai,加上一个aj之后可能大于1000也可能小于1000,那么,只需要让序列有序,这样就可以二分出第一个aj使得ai+aj>=1000。这样这个aj左右侧就可以分别打开绝对值线性相加求解,再一个个相加是不可能的,我们直接用前缀和就行。
以下是代码:
#includeusing namespace std; typedef long long ll; ll a[2000005]; ll num[2000005]; ll ans_mid; int main(){ ios::sync_with_stdio(false); ll n; cin>>n; for (int i=1;i<=n;i++) { cin>>a[i]; int x=abs(a[i]+a[i]-1000); ans_mid+=x; } sort(a+1,a+1+n); for (int i=1;i<=n;i++){ num[i]=num[i-1]+a[i]; } ll ans=0; for (int i=1;i<=n;i++){ int x=1000-a[i]; int l=1,r=n; while (l =x) r=mid; else l=mid+1; } ll m1,m2,num1,num2; if (l==n&&a[n] =x){ m1=0; m2=n; num1=0; num2=num[n]; } else { m1=l-1; m2=n-l+1; num1=num[m1]; num2=num[n]-num[m1]; } ans=ans+((1000*m1)-(num1+(m1*a[i]))); ans=ans+((num2+(m2*a[i]))-(1000*m2)); } ans+=ans_mid; ans/=2; cout<
如果能解决前缀和过大的问题,即便ai的范围扩大到1e6或者1e9,这个方法似乎也是可以做的(int64_)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)