题意:给定你n个数,然后让你确定一个四元组(i,j,k,l),使得a[i] == a[k]并且a[j] == a[l]。n <= 3000。
数据范围不大,想到枚举可以解决,但是最多是一个O(n^2)的,但是四元组我们直接枚举会超时。假如想要枚举i和k的话,我们需要两层遍历找到能使得i等于k的位置,但是这时候j和l还没确定下来,我们最好需要在一层循环解决,也是超时。
因为i和k可以说是具有相同属性的一组数,假如我们枚举j和k,这是他们所确定的属性可以看做是不同的。我们只需要去找到j前面的k,和k后面的j,即可完成,我们这时用一个二位数组pre[pos][num],来维护pos位置前的数值等于num的位置有多少个。这样就可以在O(n^2)的枚举的同时,完成答案的更新。
ps:因为我维护的这个pre数组没有记录当前位置的数,故遇到假如当前枚举的a[k] == a[j]的话,答案会多加上一个数,这个需要更新答案是特判一下即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 3e3+7;
ll pre[MAXN][MAXN];
//枚举j和k的位置 然后去找剩下的位置对应的次数
//用一个pre数组提前预处理一下
//可以O(n^2) 处理
//把四层循环变为了两重循环
int a[MAXN];
int main()
{
int T;
scanf("%d",&T);
while(T--){
int n;
scanf("%d",&n);
memset(pre,0,sizeof(pre));
for(int i = 1;i <= n;i ++) scanf("%d",&a[i]);
for(int i = 2;i <= n+1;i ++){
for(int j = i-1;j >= 1;j --)
pre[i][a[j]]++;
}
ll ans = 0;
for(int j = 2;j < n-1;j ++){
for(int k = j + 1;k < n;k ++){
ll t1 = pre[j][a[k]];
ll t2 = pre[n+1][a[j]]-pre[k][a[j]];//前面的个数记录 因此这个地方是减去
if(a[k] == a[j]) t2--;//这里因为我的pre数组没记录挡墙这个位置上 所以这个位置a[k] == a[j]的话,答案会多加
ans += t1 * t2;
}
}
printf("%lld\n",ans);
}
return 0;
}