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

先不管答案是什么,先看图

2022牛客寒假算法基础集训营1 H 牛牛看云_第1张图片

 

显然,对于从左到右的每一个数,对于原始的的公式1,只有画出来的对角线的右上侧被计算了,如果是公式2,那么被计算的部分就是全部的整个矩阵(讲的有点模糊,希望你能听懂)

所以得出结论:公式1的结果=(公式2的结果+对角线的结果)/2

这样对公式1的求解就转化为了对公式2的求解

接下来思考公式2中打开绝对值的问题

显然对于当前的ai,加上一个aj之后可能大于1000也可能小于1000,那么,只需要让序列有序,这样就可以二分出第一个aj使得ai+aj>=1000。这样这个aj左右侧就可以分别打开绝对值线性相加求解,再一个个相加是不可能的,我们直接用前缀和就行。

以下是代码:

#include
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=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_)

你可能感兴趣的:(牛客/acwing,算法,c++)