题目链接:Pleasant Pairs
大致题意
给出一个长度为n的序列a, 里面的数字各不相同且值域为[1, 2n].
问你有多少满足: i < j 并且 a[i] * a [j] = i + j 的数对.
解题思路
随便口胡一下
在比赛的时候, 我看到这个题我就在想, 怎么去给他进行变形, 使得 a[i] 和 i 有关联起来. (因为脑子中依稀记得有个 a[i] - a[j] = j - i 的题, 我们变换成 a[i] + i = a[j] + j, 就能快速求出了.)
对于这个题, 我发现好像变形的方式不太好用, 于是我重点关注到了这个题的特点: 值域的取值为[1, 2n].
此时一个复杂度为: O(n√n) 的解法就诞生了, 我们观察可以得知, (i + j) ∈ [3, 2n - 1]. 我们不妨去枚举这个答案区间, 然后对于这个区间的每一个数字x求约数, 对于每一个合法约数对(a, b), 我们去判断a和b是否都存在, 若都存在再判断是否下标相加等于这个数字x.
写完后光速WA了一发因为没有判断 a != b, 我们最后可能得到的a和b是同一个数字, 一定要注意!!!
比赛打完C题后, 我并不感觉我的做法是正解, 毕竟pretest跑了500+ms (main test跑了700+ms). 于是我又去看了看题解. 果然, 正解是一个 O(nlogn) 的做法. 下面来说一下这个做法.
这个做法就是: 对于每一个数字a, 如果存在一个数字b满足条件, 则答案一定是a的倍数(这不废话). 因此我们可以去枚举每一个数的所有倍数, 判断另外一个数字是否存在, 是否满足条件即可.
AC代码
解法一: 复杂度 O(n√n)
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
int main()
{
int t; cin >> t;
while (t--) {
unordered_map<int, int> mp; // num, index
int n; scanf("%d", &n);
rep(i, n) {
int x; scanf("%d", &x);
mp[x] = i;
}
int res = 0;
for (int num = 3; num <= 2 * n - 1; ++num) {
for (int a = 1; a <= num / a; ++a) {
if (num % a == 0) {
int b = num / a;
if (a == b) continue;
if (mp.count(a) and mp.count(b)) {
if (mp[a] + mp[b] == num) res++;
}
}
}
}
printf("%d\n", res);
}
return 0;
}
解法二: 复杂度O(nlogn)
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
int main()
{
int t; cin >> t;
while (t--) {
unordered_map<int, int> mp;
int n; scanf("%d", &n);
rep(i, n) {
int x; scanf("%d", &x);
mp[x] = i;
}
int res = 0;
for (auto& [a, i] : mp) {
for (int b = 1; b * a <= 2 * n - 1; ++b) {
if (a == b) continue;
if (mp.count(b) and mp[b] + i == b * a) res++;
}
}
printf("%d\n", res / 2); //因为没有判断i < j, 所以最后答案会重复一遍
}
return 0;
}