2022牛客寒假算法基础集训营1 H 牛牛看云

关于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_)

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值