1. 问题
我们要计算:
S=i=1∑Nj=1∑M∣Ai−Bj∣mod998244353
其中 N,M≤3×105,不能暴力 O(NM)。
2. 数学推导
对于每个固定的 Ai:
j=1∑M∣Ai−Bj∣=Bj≤Ai∑(Ai−Bj)+Bj>Ai∑(Bj−Ai)
设 k= 小于等于 Ai的 Bj的数量,S≤= 这些 Bj的和,S>= 大于 Ai的 Bj的和,且 S>=sum(B)−S≤。
则:
贡献=(Ai⋅k−S≤)+((S>)−Ai⋅(M−k))
整理:
=Ai⋅k−S≤+S>−Ai⋅M+Ai⋅k
=(2k−M)⋅Ai+(S>−S≤)
=(2k−M)⋅Ai+(sum(B)−2S≤)
3. 算法步骤
-
对 B排序
-
计算 B的前缀和数组
-
对每个 Ai:
-
在排序后的 B中二分找到第一个大于 Ai的位置 p
-
则 k=p(下标从0开始)
-
S≤=pref[p]
-
代入公式计算贡献
-
-
累加所有贡献,取模输出
4. 时间复杂度
-
排序:O(MlogM)
-
二分查找:O(NlogM)
-
总复杂度:O((N+M)logM),可行
5. 取模处理
模数 MOD=998244353,注意:
-
所有加减乘都要取模
-
处理负数:(x % MOD + MOD) % MOD
#include <bits/stdc++.h>
using namespace std;
const int MOD = 998244353;int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);int N, M; cin >> N >> M; vector<long long> A(N), B(M); for (int i = 0; i < N; i++) cin >> A[i]; for (int i = 0; i < M; i++) cin >> B[i]; // 1. 对B排序 sort(B.begin(), B.end()); // 2. 计算B的前缀和(模MOD) vector<long long> pref(M + 1, 0); for (int i = 0; i < M; i++) { pref[i + 1] = (pref[i] + B[i]) % MOD; } long long total_B_sum = pref[M] % MOD; // B的总和 long long ans = 0; // 3. 对每个A[i]计算贡献 for (int i = 0; i < N; i++) { long long ai = A[i] % MOD; // 二分查找:第一个大于ai的位置 int p = upper_bound(B.begin(), B.end(), ai) - B.begin(); // p 就是小于等于ai的元素个数 long long k = p; long long S_le = pref[p] % MOD; // 小于等于ai的部分和 // 贡献 = (2k - M) * ai + (total_B_sum - 2*S_le) long long term1 = ((2 * k - M) % MOD) * ai % MOD; long long term2 = (total_B_sum - 2 * S_le) % MOD; long long contribution = (term1 + term2) % MOD; ans = (ans + contribution) % MOD; } // 处理负数 ans = (ans % MOD + MOD) % MOD; cout << ans << "\n"; return 0;}