help a+b+c+d=0STLvector做,超时了QAQ

P1748 a+b+c+d==0
求和问题可以被看做是以下的公式,给定 A,B,C,D 四个列表,计算有多少四元组满足 (a, b, c, d) ∈ A × B × C × D 且 a + b + c + d = 0。我们推测所有的列表都有 n 个数字。
问题描述:
注:不同的四元组是指元素位置不一样的四元组

数据范围 n<=2e3

样例输入
输入的第一个数字指明有 T 组。每一组这样描述,第一行是列表大小 n, 然后有 n 行。每一行都有四个整型数字,分别属于 A,B,C,D 四列。
1
6
-45 22 42 -16
-41 -27 56 30
-36 53 -37 77
-36 30 -75 -46
26 -38 -10 62
-32 -54 -6 45

样例输出
5
对于每一个测试用例,统计有多少个四元组满足他们的和是 0 。每一组数据一行。

遇到的问题,超时。

我的思路:将a和b,c和d分别分为一组,计算出ai+bj和ci+dj所有可能的值,并用sum1和sum2进行保存。复杂度为n^2;sort 排序sum2数组复杂度为n^2logn^2;接下来用upp_bound和lower_bound函数找到-sum1[i]在sum2中个数,复杂度为n^2*logn^2。但是最后还是超时了。

我得尝试代码:

#include<iostream>
#include<string.h>
#include<vector>
#include<algorithm> 
using namespace std;
vector<int>v1;
vector<int>v2;
vector<int>v3;
vector<int>v4;
vector<int>sum1;
vector<int>sum2;
int main()
{
    int t=0;
    cin>>t;
    while(t--) //输入复杂度n 
    {
        int n=0;
        cin>>n;
        int a=0,b=0,c=0,d=0;
        for(int i=0;i<n;++i)
        {
            cin>>a>>b>>c>>d;
            v1.push_back(a);
            v2.push_back(b);
            v3.push_back(c);
            v4.push_back(d);    
        }
        
        for(int i=0;i<n;++i)      
        for(int j=0;j<n;++j)  
        {
            sum1.push_back(v1[i]+v2[j]);
        }
        
        for(int i=0;i<n;++i)  
        for(int j=0;j<n;++j)
        {
            sum2.push_back(v3[i]+v4[j]);
        }
        
        sort(sum2.begin(),sum2.end());   
        
        int ans=0;
        vector<int>::iterator s1;
        vector<int>::iterator s2;
        
        for(int i=0;i<sum1.size();++i)
        {
        s1=lower_bound(sum2.begin(),sum2.end(),-sum1[i]);
        s2=upper_bound(sum2.begin(),sum2.end(),-sum1[i]);
        ans+=s2-s1;
      }
        cout<<ans<<endl;
    }
    return 0;
 } 

问题:
根据问题规模2e3;时间复杂度应该不超过n^3;

我的代码根据我上文的尝试分析,实在n^2logn^2级别的,是不应该超时的呀。请大家帮我看看,是否是我的复杂度分析有问题。或者是否有更好的解决方法。orz

该回答通过自己思路及引用到GPTᴼᴾᴱᴺᴬᴵ搜索,得到内容具体如下:
你的问题在于在每个测试用例中都创建了新的向量,导致内存重新分配和复制,浪费了很多时间。你可以将向量的创建和清空放在 while 循环之外,这样可以避免不必要的内存分配和复制。

此外,你可以使用 unordered_map 或者 unordered_multiset 来优化查找操作。将 sum2 中的值作为 key,将其出现次数作为 value,这样查找 -sum1[i] 在 sum2 中的出现次数可以在常数时间内完成。

下面是经过修改和优化的代码:

#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
using namespace std;

vector<int> v1, v2, v3, v4;
vector<int> sum1, sum2;

int main() {
  int t;
  cin >> t;

  while (t--) {
    int n;
    cin >> n;

    v1.resize(n);
    v2.resize(n);
    v3.resize(n);
    v4.resize(n);
    sum1.clear();
    sum2.clear();

    for (int i = 0; i < n; ++i) {
      cin >> v1[i] >> v2[i] >> v3[i] >> v4[i];
    }

    for (int i = 0; i < n; ++i) {
      for (int j = 0; j < n; ++j) {
        sum1.push_back(v1[i] + v2[j]);
        sum2.push_back(v3[i] + v4[j]);
      }
    }

    unordered_map<int, int> cnt;
    for (int x : sum2) {
      ++cnt[x];
    }

    int ans = 0;
    for (int x : sum1) {
      ans += cnt[-x];
    }

    cout << ans << endl;
  }

  return 0;
}

注意,unordered_map 在最坏情况下的时间复杂度仍然是 O(n^2),但是在平均情况下可以达到 O(n) 的时间复杂度,因此可以用来优化查找操作。


如果以上回答对您有所帮助,点击一下采纳该答案~谢谢