Codeforces1541B Pleasant Pairs (思维)

题目链接: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;
}

END

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值