牛客 Diff-prime Pairs (数论+埃氏筛+前缀和)

题目描述
牛客 Diff-prime Pairs (数论+埃氏筛+前缀和)_第1张图片

输入描述:
Input has only one line containing a positive integer N.

1 ≤ N ≤ 107
输出描述:
Output one line containing a non-negative integer indicating the number of diff-prime pairs (i,j) where i, j ≤ N
示例1
输入
3
输出
2

示例2
输入
5
输出
6

首先附上官方题解
Main Idea: Math, Prime Sieve, Prefix Sum

If gcd(i_1, j_1) is not equal to gcd(i_2, j_2), (i_1, j_1) won’t be equal to (i_2, j_2).
The answer will be sum of number of diff-prime pairs whose gcd is g for each g.

Iterate each g, and find two distinct prime p_1, p_2. Then, (g p_1, g p_2) will be an answer if g p_1 <= N and g p_2 <= N. It will be reduced to find the number of prime within (N/g). It can be done by prime sieve and prefix sum.
Since p_1 not equal to p_2, it’s obvious that gcd(g p_1, g p_2)=g
Overall Time complexity: O(N) Overall Space complexity: O(N)

题目分析:这道题目的难度只有一颗星,但是题目给的数据范围达到了1e7之多,暴力的时间复杂度达到了O(n^2),显然是不行的,所以这个题目需要优化+转化,我们不能被题目的表现所迷惑,题目里面有gcd,但是如果去求gcd的话时间复杂度肯定很高,所以这里用了十分巧妙地方法

  • 首先我们用埃氏筛去筛选素数,这是一种十分强大的方法,每次都筛去素数的倍数
  • 然后对其求前缀和,我们把素数的个数求出来,方法后面求素数对
  • 接下来就是题目的核心思想,我们要求一共有所少个素数对,首先素数对中的素数是不能相同的,其次我们枚举每一个公约数,因为素数对的倍数也是符合题目要求,这样只需要求(2,n/g)范围就行,ans += sum[n / g] * (sum[n / g] - 1);减一的操作就是因为用了排列组合数学的思想
  • 遍历每个g,找到两个不同的素数p1,p2,如果g * p1<=n 并且 g * p2<=n,那么(g * p1,g * p2)就是一个符合要求的素数对,那问题就转化成找 n/g 以内的素数的数量,可以通过线性筛出素数再处理前缀和得出。
  • 对于每个含有公因子g的数,都去求一次素数对
#include 
using namespace std;
typedef long long ll;
const int maxn = 1e7 + 10;
int prime[maxn], sum[maxn];
void solve(int x)
{
	prime[0] = prime[1] = 1;
	for (int i = 2; i <= x; ++i)
	{
		if (!prime[i])
		{
			sum[i] = 1;
			for (int j = 2 * i; j <= x; j += i)
				prime[j] = 1;
		}
	}
}
int main()
{
	ll n, ans = 0;
	scanf("%lld", &n);
	solve(n);
	for (int i = 1; i <= n; ++i)
		sum[i] += sum[i - 1];
	for (int g = 1; g <= n; ++g)
		ans += sum[n / g] * (sum[n / g] - 1);
	printf("%lld\n", ans);
}
温柔的晚风 傍晚的晚霞 解暑的西瓜 冒泡的可乐 人间的美好多着呢 不要为眼前的黑暗所迷惑 要相信自己值得世间所有美好。

你可能感兴趣的:(牛客,数论)