【AtCoder Beginner Contest 353】C - Sigma Problem 题解

【AtCoder Beginner Contest 353】C - Sigma Problem 题解

题目大意

给出一个函数 f ( x , y ) = ( x , y ) f(x,y) = (x,y) f(x,y)=(x,y),求: ∑ i = 1 N − 1 ∑ j = i + 1 N f ( A i , A j ) \displaystyle \sum_{i=1}^{N-1}\sum_{j=i+1}^N f(A_i,A_j) i=1N1j=i+1Nf(Ai,Aj).

大致思路

由于给出的函数 f ( x , y ) f(x,y) f(x,y) 是将两个数字相加,而加法是可交换的,那么 f ( x , y ) = f ( y , x ) f(x,y)=f(y,x) f(x,y)=f(y,x) 。然后求和要求的是任意两个不同数字经过函数运算后相加的结果,那么任意交换给出的数字是不会改变答案的。

因此,为了便于处理,可以把数组排序。

排序完成后,可以先维护前缀和, 因为不难发现, 对于每个a[i], 均需要与前面每个数字进行一次函数运算, 那么在不考虑取模的情况下,产生的结果为 p r e [ i − 1 ] + ( i − 1 ) × a [ i ] pre[i-1]+(i-1) \times a[i] pre[i1]+(i1)×a[i] , 其中
p r e [ i − 1 ] pre[i-1] pre[i1] 表示前 1 1 1 个数字的前缀和。

然后考虑取模需要减掉的部分,不难想到,对于 x + y < 1 0 8 x+y<10^8 x+y<108 的情况,是不需要进行取模的,而 x + y ≥ 1 0 8 x+y≥10^8 x+y108 时,则每次运算的取模均相当于减去一个 1 0 8 10^8 108 ,由于数组已经经过排序了,那么只需要通过二分对 1 0 8 − a [ 2 ] 10^8-a[2] 108a[2] 进行查找,就可以知道需要减去的数字所在的起点是多少,那么这个查找到的位置到下标 i − 1 i-1 i1 之间的所有数字与当前数字a[i]进行运算,均需要减掉 1 0 8 10^8 108 , 令 cnt 为需要减去的数量,则a[i]产生的贡献为 p r e [ i − 1 ] + ( i − 1 ) × a [ i ] − c n t × 1 0 8 pre[i - 1] + (i - 1) \times a[i] - cnt \times 10^8 pre[i1]+(i1)×a[i]cnt×108

代码

long long n;
long long ans = 0;
long long a[N];
long long pre[N];
void solve(){
    // 竞赛程序
    cin >> n;
    for (long long i = 1; i <= n; i++) {
        cin >> a[i];
    }
    sort(a + 1, a + n + 1);
    for (long long i = 1; i <= n; i++) {
        pre[i] = pre[i - 1] + a[i];
    }
    for (long long i = 2; i <= n; i++) {
        int usen = lower_bound(a + 1, a + i, mod - a[i]) - a;
        ans += pre[i - 1] + (i - 1) * a[i] - (mod * (i - usen));
    }
    cout << ans << endl;
}

happy happy happy

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值