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)\) 是特殊对,则有
其中 \(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)\) 的个数等于
其中 \(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))\) 。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/927436.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!