T1. 整除

创建一个由数对组成的数组 \(C\),其中每个元素为 \((i, a_i) \ (1 \leqslant i \leqslant n)\)。令 \(C_{x, 1}\)\(C_{x_, 2}\) 分别表示数对 \(C_x\) 的第一个(即 \(i\))和第二个元素(即 \(a_i\))。

若两数对 \(C_i, C_j\) 满足 \(C_{i,1} \cdot C_{j,1} \Big| C_{i,2} \cdot C_{j,2}\),则称这对数对为特殊数对。
接下来我们要计算所有这样的无序对 \((C_i, C_j)\) 的总数。

对于所有数对 \(C_i\),计算 \(C_{i,1}\)\(C_{i,2}\) 的最大公约数 \(\gcd(C_{i,1}, C_{i,2})\),将每个数对的两个元素同时除以这个最大公约数。记处理后的新数对数组为 \(D\)

命题:

\((D_i,D_j)\) 是特殊对,当且仅当 \((C_i,C_j)\) 是特殊对。

证明:

为清晰起见,令 \(C_i = (a, b)\)\(C_j = (c, d)\);设 \(g_1 = \gcd(a, b)\)\(g_2 = \gcd(c, d)\) 。最后定义 \(D_i = (a', b')\)\(D_j = (c', d')\),其中 \(a = g_1 \cdot a'\)\(b = g_1 \cdot b'\)\(c = g_2 \cdot c'\),以及 \(d = g_2 \cdot d'\)

然后, \((C_i, C_j)\) 是特殊对 \(\Rightarrow \ a \cdot c \Big| b \cdot d\) \(\Rightarrow \, (g_1 \cdot a') \cdot (g_2 \cdot c') \Big| (g_1 \cdot b') \cdot (g_2 \cdot d')\) \(\Rightarrow \, a' \cdot c' \Big| b' \cdot d'\) \(\Rightarrow\) \((D_i, D_j)\) 也是特殊对。

因此,原问题等价于求 \(D\) 中特殊无序数对的数量。

命题:

\((D_i, D_j)\) 是特殊对,当且仅当 \(D_{i,1} \Big| D_{j,2}\)\(D_{j,1} \Big| D_{i,2}\)

(证明是显然的,作为练习留给读者;只需利用 \(D_{i,1}\)\(D_{i,2}\) 互质的事实即可。)

所以,如果 \((D_i, D_j)\) 是特殊对,则有

\[\begin{aligned} D_{i,1} \Big| D_{j,2} \ \Rightarrow \ D_{i,1} = D'_{j,2}\\ D_{j,1} \Big| D_{i,2} \ \Rightarrow \ D_{j,1} = D'_{i,2} \end{aligned} \]

其中 \(D'_{i,2}\)\(D'_{j,2}\) 分别是 \(D_{i,2}\)\(D_{j,2}\) 的因子。

\(dp_i[x][y]\) 表示使得 \(x = D_{j,1}\)\(y \Big| D_{j,2}\)\(j(j < i)\) 的数量

利用上述推导,不难看出使得 \((D_i, D_j)\) 为特殊对的 \(j(j < i)\) 的个数等于

\[\sum_{f \in F} dp_i[D_{i,1}][f] \]

其中 \(F\)\(D_{i,2}\) 的所有因子的集合
将所有 \(i\) 对应的该值求和,即可得到答案。

代码实现
#pragma GCC optimize ("O2")
#pragma GCC optimize ("unroll-loops")
#pragma GCC target ("avx2")
#include <bits/stdc++.h>

using namespace std;
using ll = long long;

vector<int> factor(int n) {
    vector<int> res;
    for (int i = 1; i*i <= n; ++i) {
        if (n%i) continue;
        res.push_back(i);
        if (i*i != n) res.push_back(n/i);
    }
    return res;
}

void solve() {
    int n;
    cin >> n;
    
    vector<pair<int, int>> ps;
    for (int i = 1; i <= n; ++i) {
        int x;
        cin >> x;
        int g = gcd(i, x);
        ps.emplace_back(i/g, x/g);
    }
    
    ll ans = 0;
    unordered_map<int, unordered_map<int, int>> dp;
    for (auto [a, b] : ps) {
        auto ds = factor(b);
        for (int d : ds) {
            ans += dp[a][d];
        }
        for (int d : ds) {
            dp[d][a]++;
        }
    }
    
    cout << ans << '\n';
}

int main() {
    cin.tie(nullptr) -> sync_with_stdio(false);
    
    int t;
    cin >> t;
    
    while (t--) solve();
    
    return 0;
}

T2. MEX

考虑从小到大往序列中填数。

初始时,我们假设每个元素的值均为 \(-1\),然后按 \(0\)\(n-1\) 的顺序填数,并分析这样填数时每个数对答案的贡献。为了方便,我们设 \(i\) 在序列中的下标是 \(p_i\)

假设当前填的数是 \(x\) 。设 \(p=\max(p_0, p_1, \cdots, p_x)\),那么对于 \(i \geqslant p\) 的所有下标 \(i\)\(\mathrm{mex}(a_1, a_2, \cdots , a_i)\) 的值都会从 \(x\) 变成 \(x+1\) ,所以这个数对答案的贡献是 \(n-p+1\)

这使得我们可以直接设 \(f(x, p, k)\) 为,已经在序列中填了 \(0 \sim x\) 的数, \(\max(p_0, p_1, \cdots, p_x) = p\) ,当前的答案为 \(k\) 的方案数。

我们考虑哪些状态可以转移到 \(f(x, p, k)\) 。存在两种情况:

  • \(p\) 被更新了,即 \(x\)\(0, 1, \cdots, x-1\) 里所有数的右边。贡献为 \(\sum\limits_{i = 1}^{p-1} f(x-1, i, k-(n-p+1))\)
  • \(p\) 没有被更新,即存在 \(0, 1, \cdots, x-1\) 的数在 \(x\) 的右边。这样的话 \(x\) 填的位置有 \(p-x\) 种选择,那么贡献为 \((p-x) \cdot f(x-1, p, k-(n-p+1))\)

总复杂度为 \(O(n^2(k-n))\)

T3. 三人竞赛

不妨只考虑Alice的答案。
\(x_i = a_i-b_i\)\(y_i = a_i-c_i\),那么对于区间 \([l, r]\),原条件可以写成 \(\sum\limits_{i=l}^r x_i \geqslant 0\)\(\sum\limits_{i=l}^r x_i \geqslant 0\)
然后我们设 \(X_i = \sum\limits_{j=1}^i x_j\)\(Y_i = \sum\limits_{j=1}^i y_j\),那么条件可以进一步改写为 \(X_r \geqslant X_{l-1}\)\(Y_r \geqslant Y_{l-1}\)
然后其实就变成了,查询有多少对 \((p, q)\) 使得 \(p < q\)\(X_p \leqslant X_q\)\(Y_p \leqslant Y_q\)。满足这个条件的每一对 \((p, q)\) 都对应一个合法的区间 \([p+1, q]\)
于是这样就转化为了一道经典的三维偏序问题。
时间复杂度为 \(O(n\log n)\)