Educational Codeforces Round 94 (Rated for Div. 2) D. Zigzags【枚举 | 统计技巧】

本文探讨了一种特定的四元组匹配问题,给出了三种不同的枚举方法来解决该问题,并提供了相应的C++代码实现。问题的核心在于找到满足特定条件的数组元素组合。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

给出一个数组 a 1 , a 2 … a n a_1, a_2 \dots a_n a1,a2an,计算满足下列条件的元组 ( 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; 1i<j<k<ln;
  • 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 4n3000, 1 ≤ a i ≤ n 1 \le a_i \le n 1ain


思路:

  • 由于 4 ≤ n ≤ 3000 4 \le n \le 3000 4n3000,因此支持 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 k2 开始枚举,对于统计答案的方法,很巧妙,我们先用统计数组 c n t cnt cnt 统计出 k k k 后面各个数字的数量,设计一个变量 c u r cur cur,初始值为 c n t [ a k − 1 ] cnt[a_{k-1}] cnt[ak1],表示 a k − 1 a_{k-1} ak1 这个数字与 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 c1c2 分别统计出 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 c1c2 是一直变化的,因此 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值