ABC353C Sigma Problem 題解
題目連結:AT
題目中的兩個求和符號 \(\sum_{i=1}^{N-1} \sum_{j=i+1}^{N}\) 實際上是在列舉所有的有序數對 \((i, j)\)。而有序數對的個數 \(N(N-1)/2 = O(N^{2})\),真的去列舉所有數對肯定會 T。這時應該考慮去拆貢獻,求出每個 \(A_i\) 對答案的貢獻。
這道題的一個要點是要注意到題目中 \(A_i < 10^8\)。也就是說,對於任意的 \(A_i, A_j\),要麼 \(A_i + A_j < 10^8\),此時有 \(f(A_i, A_j) = A_i + A_j\);要麼 \(10^8 \le A_i + A_j < 2 \times 10^8\),此時有 \(f(A_i, A_j) = A_i + A_j - 10^8\)。
現在暫時不考慮第二種情況。這時所求的答案實際上就是列舉所有有序數對 \((i, j)\),求和 \(A_i + A_j\)。因為每個 \(A_i\) 恰好出現在 \(N-1\) 個數對中,所以此時的答案是 \((N-1) \sum_{i=1}^{N} A_i\)。
(為什麼每個 \(A_i\) 恰好出現在 \(N-1\) 個數對中?原因十分簡單:因為我們列舉了所有的有序數對,所以每個 \(A_i\) 一定和除了它本身之外的所有 \(N-1\) 個元素都“配對”過一次,因此就出現在 \(N-1\) 個數對中。當然,因為是有序數對,可能 \(A_i\) 有時是數對的第一個元素,有時是數對的第二個元素,但這顯然不影響答案。)
如果考慮第二種情況,最終的答案就要減去若干個 \(10^8\)。具體減去多少個呢?對於某個 \(A_i\),找出所有滿足 \(A_i + A_j \ge 10^8\) 且 \(j > i\) 的 \(j\) 的個數,那麼 \(A_i\) 就對答案貢獻了這麼多個 \(-10^8\)。(這裡限定 \(j > i\) 是因為題目中要求有序數對,不這麼限定會導致重複計算。)於是可以先把 \(A\) 陣列排序,對於某個 \(A_i\),就可以二分查詢出第一個滿足\(A_i + A_j \ge 10^8\) 且 \(j > i\) 的 \(j_0\),於是滿足條件的 \(j\) 的總數就是 \(N-j_0+1\)。
(為什麼排序不會改變答案?要點在於題目中的 \(f(x, y)\) 函式滿足“交換律”,或者說 \(f(x, y) = f(y, x)\),所以可以隨意調換陣列元素的順序而不改變答案。與之區分的是本場比賽的 D 題,那道題和本題很像,都是列舉所有的有序數對 \((i, j)\),然後求值某個二元函式的函式值的和。但 D 題的函式不滿足這種“交換律”,所以不能先排序再求和。)
綜上,我們先求出所有 \(A_i\) 的和的 \(N-1\) 倍,再列舉所有的 \(A_i\),找出應該減去多少個 \(10^8\) 即可。時間複雜度 \(O(N \log N)\),瓶頸在排序和二分查詢。
程式碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
constexpr int MAXN = 3e5 + 10;
constexpr ll MOD = 1e8;
int n;
ll a[MAXN], ans, cnt;
int main()
{
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i], ans += (n-1) * a[i];
sort(a + 1, a + n + 1);
for(int i = 1; i < n; i++)
{
ll x = MOD - a[i];
int pos = lower_bound(a + i + 1, a + n + 1, x) - a; // 注意這裡是從 a[i+1] 開始找的,這樣能確保不會重複計算
cnt += n - pos + 1;
}
ans -= cnt * MOD;
cout << ans << endl;
return 0;
}
提交記錄