关于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左右侧就可以分别打开绝对值线性相加求解,再一个个相加是不可能的,我们直接用前缀和就行。
以下是代码:
#include<bits/stdc++.h>
using 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<r){
int mid=(l+r)/2;
if (a[mid]>=x) r=mid;
else l=mid+1;
}
ll m1,m2,num1,num2;
if (l==n&&a[n]<x){
m1=n;
m2=0;
num1=num[n];
num2=0;
}
else if (l==1&&a[1]>=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<<ans<<'\n';
return 0;
}
如果能解决前缀和过大的问题,即便ai的范围扩大到1e6或者1e9,这个方法似乎也是可以做的(int64_)