题意:
给出一个数组 a 1 , a 2 … a n a_1, a_2 \dots a_n a1,a2…an,计算满足下列条件的元组 ( i , j , k , l ) (i, j, k, l) (i,j,k,l) 的个数:
- 1 ≤ i < j < k < l ≤ n ; 1 \le i < j < k < l \le n; 1≤i<j<k<l≤n;
- a i = a k a_i = a_k ai=ak and a j = a l ; a_j = a_l; aj=al;
其中: 4 ≤ n ≤ 3000 4 \le n \le 3000 4≤n≤3000, 1 ≤ a i ≤ n 1 \le a_i \le n 1≤ai≤n。
思路:
- 由于 4 ≤ n ≤ 3000 4 \le n \le 3000 4≤n≤3000,因此支持 O ( n 2 ) \mathcal{O}(n^2) O(n2) 的算法,因此我们最多可以枚举四元组中的其中两个值。
- 枚举方法1: 枚举每个 j , k j, k j,k 从 j + 1 j + 1 j+1 开始枚举, 用两个统计数组 c 1 , c 2 c_1, c_2 c1,c2 分别维护 j j j 的前面、 k k k 的后面各个数字的数量,如果 c 1 [ a k ] > 0 c_1[a_k] > 0 c1[ak]>0 且 c 2 [ a j ] > 0 c_2[a_j] > 0 c2[aj]>0,表示此时 j j j、 k k k 的位置存在四元组,存在的个数为 c 1 [ a k ] ⋅ c 2 [ a j ] c1[a_k] \cdot c2[a_j] c1[ak]⋅c2[aj] 。
- 枚举方法2: 枚举每个 k k k, i i i 从 k − 2 k - 2 k−2 开始枚举,对于统计答案的方法,很巧妙,我们先用统计数组 c n t cnt cnt 统计出 k k k 后面各个数字的数量,设计一个变量 c u r cur cur,初始值为 c n t [ a k − 1 ] cnt[a_{k-1}] cnt[ak−1],表示 a k − 1 a_{k-1} ak−1 这个数字与 k k k 后面多少个数字相等,如果 a [ i ] a[i] a[i] 与 a [ k ] a[k] a[k] 相等,表示 i i i 和 k k k 之间的 j j j 和 k k k 后面的 l l l 构成的二元组( a j = a l a_j = a_l aj=al)的数量都要被统计上,这个数量就是 c u r cur cur,因此,每次 i i i 向左移动前, c u r cur cur 要加上 c n t [ a i ] cnt[a_i] cnt[ai]。
- 枚举方法3: 枚举每个 i i i, k k k 从 i + 2 i + 2 i+2 开始枚举,统计答案的方法的原理与 方法2 相同,但是由于枚举方式的不同,该统计方法需要加上一步,我们用统计数据 c 1 、 c 2 c1、c2 c1、c2 分别统计出 k k k 前面、 k k k 后面各个数字的数量,设计一个变量 c u r cur cur,初始值为 c 1 [ a i + 1 ] c1[a_{i+1}] c1[ai+1],表示 a i + 1 a_{i+1} ai+1 这个数字与 k k k 后面多少个数字相等,如果 a [ i ] a[i] a[i] 与 a [ k ] a[k] a[k] 相等,表示 i i i 和 k k k 之间的 j j j 和 k k k 后面的 l l l 构成的二元组( a j = a l a_j = a_l aj=al)的数量要被统计上,这个数量就是 c u r cur cur,但是因为 k k k 每次向右移动,即 c 1 、 c 2 c1、c2 c1、c2 是一直变化的,因此 k k k 移动后将 c 2 c2 c2 中 a k a_k ak 的数量减去 1 1 1, c u r cur cur 在计入答案前首先减去 c 1 [ a k ] c1[a_k] c1[ak](由于 a k a_k ak 已经不在 c 2 c2 c2 中了,而之前对于 i i i 满足 a i = a k a_i = a_k ai=ak 每个已经加在 cur 上的 c 2 [ a i ] c2[a_i] c2[ai] 其中的一个 1 1 1 就是 a k a_k ak 产生的贡献,因此要减去这些贡献)。若此时 a [ i ] = a [ k ] a[i] = a[k] a[i]=a[k],则将 c u r cur cur 计入答案,若不相等,则不计入答案。然后 c u r cur cur 要加上 a k a_k ak 与后面的 l l l ( l > k l > k l>k) 带来的二元组数量(加上 c 2 [ a k ] c2[a_k] c2[ak],解释类似方法2),并且将 c 1 c1 c1 中 a k a_k ak 的数量加上 1 1 1。
AC Code:
方法1:
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
//#include <unordered_set>
//#include <unordered_map>
#include <deque>
#include <list>
#include <iomanip>
#include <algorithm>
#include <fstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <assert.h>
using namespace std;
typedef long long ll;
//cout << fixed << setprecision(2);
//cout << setw(2);
const int N = 2e5 + 6, M = 1e9 + 7, INF = 0x3f3f3f3f;
int main() {
//freopen("/Users/xumingfei/Desktop/ACM/test.txt", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
a[i]--;
}
vector<int> c1(n), c2(n);
c1[a[0]]++;
ll ans = 0;
for (int j = 1; j < n - 2; j++) {
fill(c2.begin(), c2.end(), 0);
for (int k = j + 1; k < n; k++) {
c2[a[k]]++;
}
for (int k = j + 1; k < n; k++) {
c2[a[k]]--;
if (c1[a[k]] > 0 && c2[a[j]] > 0) {
ans += 1LL * c1[a[k]] * c2[a[j]];
}
}
c1[a[j]]++;
}
cout << ans << '\n';
}
return 0;
}
方法2:
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
//#include <unordered_set>
//#include <unordered_map>
#include <deque>
#include <list>
#include <iomanip>
#include <algorithm>
#include <fstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <assert.h>
using namespace std;
typedef long long ll;
//cout << fixed << setprecision(2);
//cout << setw(2);
const int N = 2e5 + 6, M = 1e9 + 7, INF = 0x3f3f3f3f;
int main() {
//freopen("/Users/xumingfei/Desktop/ACM/test.txt", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
a[i]--;
}
vector<int> cnt(n);
ll ans = 0;
for (int k = 2; k < n - 1; k++) {
fill(cnt.begin(), cnt.end(), 0);
for (int l = k + 1; l < n; l++) {
cnt[a[l]]++;
}
ll cur = 0;
cur += cnt[a[k - 1]];
for (int i = k - 2; i >= 0; i--) {
if (a[i] == a[k]) ans += cur;
cur += cnt[a[i]];
}
}
cout << ans << '\n';
}
return 0;
}
方法3:
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
//#include <unordered_set>
//#include <unordered_map>
#include <deque>
#include <list>
#include <iomanip>
#include <algorithm>
#include <fstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <assert.h>
using namespace std;
typedef long long ll;
//cout << fixed << setprecision(2);
//cout << setw(2);
const int N = 2e5 + 6, M = 1e9 + 7, INF = 0x3f3f3f3f;
int main() {
//freopen("/Users/xumingfei/Desktop/ACM/test.txt", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i<n;i++) {
cin >> a[i];
--a[i];
}
ll ans = 0;
vector<int> c1(n), c2(n);
for (int i = 0; i < n - 3; i++) {
fill(c1.begin(), c1.end(), 0);
fill(c2.begin(), c2.end(), 0);
for (int l = i + 2; l < n; l++) {
c2[a[l]]++;
}
ll cur = 0;
cur += c2[a[i + 1]];
c1[a[i + 1]]++;
for (int k = i + 2; k < n - 1; k++) {
cur -= c1[a[k]];
c2[a[k]]--;
if (a[i] == a[k]) ans += cur;
cur += c2[a[k]];
c1[a[k]]++;
}
}
cout << ans << '\n';
}
return 0;
}