【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=1∑N−1j=i+1∑Nf(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[i−1]+(i−1)×a[i] , 其中
p
r
e
[
i
−
1
]
pre[i-1]
pre[i−1] 表示前
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+y≥108 时,则每次运算的取模均相当于减去一个
1
0
8
10^8
108 ,由于数组已经经过排序了,那么只需要通过二分对
1
0
8
−
a
[
2
]
10^8-a[2]
108−a[2] 进行查找,就可以知道需要减去的数字所在的起点是多少,那么这个查找到的位置到下标
i
−
1
i-1
i−1 之间的所有数字与当前数字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[i−1]+(i−1)×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