一、题意

给定4个n元素(1<=n<=4000)的集合A,B,C,D,要求分别从中选取一个元素a,b,c,d,使得a+b+c+d=0。
问有多少种取法。

二、解析

直接4重循环暴力肯定超时。
回想若只有两个集合时,我们会通过map来存储其中一个集合A的值,然后遍历另外一个集合B来从map中询问是否有-b存在。
在这里也可以用类似的思想。

先用二重循环遍历集合A,B,然后把选取的a+b的值放入map中,然后再用二重循环遍历C,D,去询问map中有无-c-d存在。
时间复杂度为O(n^2lgn),由于n<=4000,还是会有超时风险,因此改用unordered_map即可。

三、代码

#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
using LL = long long;
int kase, n;

int main() {
    cin >> kase;
    while(kase --) {
        cin >> n;
        vector<int> A, B, C, D;
        for(int i = 0, a, b, c, d; i < n; i ++) {
            cin >> a >> b >> c >> d;
            A.push_back(a), B.push_back(b), C.push_back(c), D.push_back(d);
        }
        unordered_map<int, LL> sum_cnt;
        for(auto a : A) for(auto b : B) sum_cnt[a + b] ++;
        LL ans = 0;
        for(auto c : C) for(auto d : D) if(sum_cnt.count(-c - d)) ans += sum_cnt[-c - d];
        cout << ans << endl;
        if(kase) cout << endl;
    }
}

四、归纳

  • 紫薯中,这种方法叫中途相遇法,感觉还挺常见的。