专栏文章

题解:P10390 [蓝桥杯 2024 省 A] 因数计数

P10390题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mipvjad7
此快照首次捕获于
2025/12/03 18:38
3 个月前
此快照最后确认于
2025/12/03 18:38
3 个月前
查看原文

因数计数

一道组合数学题。
使用 cnt[i]cnt[i] 统计 ii 的出现频率,suf[i]suf[i] 表示 ii 的倍数的个数(不包括 ii 自身),pre[i]pre[i] 表示 ii 的因数的个数(不包括 ii 自身)。其中 sufsufprepre 数组可通过 cntcnt 数组在 O(NlnN)O(N\ln N) 复杂度内得出(具体实现看代码即可)。
  1. 首先统计所有的二元组 (i,j)ij(i,j)\land i\neq j 的个数 ansans ,其中 aia_iaja_j 的因数。
    • 对于 ai=aja_i=a_j ,形成的二元组个数为 Acnt[ai]2A_{cnt[a_i]}^2
    • 对于 aiaja_i\neq a_j ,形成的二元组个数为 cnt[ai]cnt[aj]cnt[a_i]\cdot cnt[a_j]
  2. 根据二元组的个数,统计所有可以形成的四元组个数: Aans2A_{ans}^2
  3. 现在看看我们统计的所有四元组里面有哪些不合法项,现在我们假设两个二元组 (i,j)(i,j)(k,l)(k,l) 形成了一个四元组 (i,j,k,l)(i,j,k,l)
    • 首位数字重复使用(这里的重复是指对同一个下标的数字使用了两次):如果一个数 xxsuf[x]>1suf[x]>1 ,会出现 i=ji=j 的情况,所占数量为 suf[x](suf[x]1)cnt[x]suf[x]\cdot (suf[x] - 1)\cdot cnt[x] (如 [2,4,6][2,4,6] 会组成四元组 (1,2,1,3)(1,2,1,3))。
    • 末尾数字重复使用:如果一个数 xxpre[x]>1pre[x]>1 ,会出现 k=lk=l 的情况,所占数量为 pre[x](pre[x]1)cnt[x]pre[x]\cdot (pre[x] - 1)\cdot cnt[x] (如 [2,3,6][2,3,6] 会组成四元组 (1,3,2,3)(1,3,2,3))。
    • 中间位数字重复使用:如果一个数 xxpre[x]>1suf[x]>1pre[x]>1\land suf[x]>1 ,会出现 j=kj=k 的情况,所占数量为 2pre[x]suf[x]cnt[x]2\cdot pre[x]\cdot suf[x]\cdot cnt[x] (乘2是因为两个二元组之间有顺序)(如 [2,4,8][2,4,8] 会组成四元组 (1,2,2,3)(1,2,2,3))。
      由于sufsufprepre 数组没有统计 xx 自身的个数,我们需要计入下面的不合法项,考虑情况同上:
    • 对于多次出现的数字 xx 的首位,末位,中间位数字重复使用:如果一个数 xx(cnt[x]>1pre[x]>0)(cnt[x]>1suf[x]>0)(cnt[x]>1\land pre[x]>0)\lor(cnt[x]>1\land suf[x]>0) ,会出现 i=ji=jj=kj=k 的情况,所占数量为 4(pre[x](pre[x]1)cnt[x]+suf[x](suf[x]1)cnt[x])4(pre[x]\cdot (pre[x] - 1)\cdot cnt[x]+suf[x]\cdot(suf[x]-1)\cdot cnt[x])(可以计算每种情况出现的次数均为 2pre/suf[x](pre/suf[x]1)cnt[x]2\cdot pre/suf[x]\cdot (pre/suf[x] - 1)\cdot cnt[x])。
    • 如果 cnt[x]>1cnt[x]>1 ,会出现 i=lj=ki=l\land j=k 的情况,所占数量为 cnt[x](cnt[x]1)cnt[x] \cdot (cnt[x] - 1) (如 [2,2][2,2] 会组成四元组 (1,2,2,1)(1,2,2,1))。
    • 如果 cnt[x]>2cnt[x]>2 ,会出现 i=jj=ki=j \lor j=k 的情况,实际上就是三个数字都相同的首位,末位,中间位数字重复使用。各占 2cnt[x](cnt[i]1)(cnt[i]2)2\cdot cnt[x] \cdot (cnt[i] - 1) \cdot (cnt[i] - 2) ,一共 4cnt[x](cnt[i]1)(cnt[i]2)4\cdot cnt[x] \cdot (cnt[i] - 1) \cdot (cnt[i] - 2) (如 [2,2,2][2,2,2] 会组成四元组 (1,1,2,3),(1,2,3,3),(1,2,2,3)(1,1,2,3),(1,2,3,3),(1,2,2,3) 等)。
所有情况统计完毕,将所有四元组的数量减去不合法的情况即可。
代码如下:
CPP
// #pragma GCC optimize(3,"Ofast","inline")
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <algorithm>
#include <string>
#include <array>
#include <vector>
#include <stack>
#include <deque>
#include <queue>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <bitset>
#include <functional>
#include <thread>
#include <chrono>
#include <random>
#include <numeric>
#include <cstring>
#include <utility>
#include <cassert>

#define fi first
#define se second
using std::cin;
using std::cout;
using std::string;
using PII = std::pair<int, int>;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
const int INF = 0x3f3f3f3f;
const double esp = 1e-5;

template <typename T>
string interage_to_string(T x) {
    string ret;
    while (x) {
        ret += x % 10 + '0';
        x /= 10;
    }
    if (ret.empty()) {
        ret = "0";
    }
    std::reverse(ret.begin(), ret.end());
    return ret;
}
int main() {
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int n;
    const int N = 1e5 + 1;
    cin >> n;
    std::vector<int> a(n), cnt(N);
    std::vector<int> pre(N), suf(N);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        cnt[a[i]]++;
    }
    u128 ans = 0;
    for (int i = 1; i < N; i++) {
        if (cnt[i]) {
            ans += 1ll * cnt[i] * (cnt[i] - 1);
            for (int j = 2; i * j < N; j++) {
                if (cnt[i * j]) {
                    ans += 1ll * cnt[i] * cnt[i * j];
                    suf[i] += cnt[i * j];
                    pre[i * j] += cnt[i];
                }
            }
        }
    }
    ans = ans * (ans - 1);
    for (int i = 1; i < N; i++) {
        if (!cnt[i]) continue;
        if (suf[i] > 1) {
            ans -= 1ll * suf[i] * (suf[i] - 1) * cnt[i];
        }
        if (pre[i] > 1) {
            ans -= 1ll * pre[i] * (pre[i] - 1) * cnt[i];
        }
        if (pre[i] && suf[i]) {
            ans -= 1ll * cnt[i] * pre[i] * suf[i] * 2;
        }
        if (cnt[i] > 1 && pre[i]) {
            ans -= 1ll * (cnt[i] - 1) * cnt[i] * pre[i] * 2;
            ans -= 1ll * pre[i] * (cnt[i] - 1) * cnt[i] * 2;
        }
        if (cnt[i] > 1 && suf[i]) {
            ans -= 1ll * (cnt[i] - 1) * cnt[i] * suf[i] * 2;
            ans -= 1ll * suf[i] * (cnt[i] - 1) * cnt[i] * 2;
        }
        if (cnt[i] >= 2) {
            ans -= 1ll * cnt[i] * (cnt[i] - 1);
        }
        if (cnt[i] >= 3) {
            ans -= 1ll * cnt[i] * (cnt[i] - 1) * (cnt[i] - 2) * 4;
        }
    }
    cout << interage_to_string(ans) << "\n";
    return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...