素数检测、因式分解

目录

一,素数检测

CSU 1030 素数槽

CSU 2051 Num

力扣 2614. 对角线上的质数

力扣 2601. 质数减法运算

力扣 3115. 质数的最大距离

二,因式分解

CSU 1522 Extravagant number

CodeForces 27E 谁有N个因数?

力扣 1998. 数组的最大公因数排序(集合并查集)

力扣 952. 按公因数计算最大组件大小(集合并查集)

力扣 2709. 最大公约数遍历(集合并查集)

力扣 829. 连续整数求和

力扣 1390. 四因数

力扣 1492. n 的第 k 个因子

力扣 2427. 公因子的数目

力扣 2507. 使用质因数之和替换后可以取到的最小值

力扣 2521. 数组乘积中的不同质因数数目

力扣 2183. 统计可以被 K 整除的下标对数目

力扣 2584. 分割数组使乘积互质

力扣 2818. 操作使得分最大


一,素数检测

素数检测就是给定一个正整数n,判断其是不是素数。

最简单方案:

枚举不超过sqrt(n)的所有素数,判断有没有n的因子。

bool isPrime(int n)
{
	if (n == 2)return true;
	if (n % 2 == 0)return false;
	for (int i = 3; i*i <= n; i += 2) if (n%i == 0)return false;
	return true;
}

CSU 1030 素数槽

题目:

Description

       处于相邻的两个素数p和p + n之间的n - 1个连续的合数所组成的序列我们将其称为长度为n的素数槽。例如,‹24, 25, 26, 27, 28›是处于素数23和素数29之间的一个长度为6的素数槽。

       你的任务就是写一个程序来计算包含整数k的素数槽的长度。如果k本身就是素数,那么认为包含k的素数槽的长度为0。

Input

第一行是一个数字n,表示需要测试的数据的个数。后面有n行,每行是一个正整数k, k大于1并且小于或等于的第十万个素数(也就是1299709)。

Output

对于输入部分输入的每一个k,都对应输出一个非负整数,表示包含k的素数槽的长度,每个非负整数占一行。

Sample Input

5
10
11
27
2
492170
Sample Output

4
0
6
0
114


代码:
 

#include<iostream>
#include<stdio.h>
using namespace std;
 
bool isPrime(int n)
{
	if (n == 2)return true;
	if (n % 2 == 0)return false;
	for (int i = 3; i*i <= n; i += 2) if (n%i == 0)return false;
	return true;
}
 
int main()
{
	int t;
	scanf("%d", &t);
	int n;
	while (t--)
	{
		scanf("%d", &n);
		int l = 0;
		for (int i = 0; !isPrime(n + i); i++) l++;
		for (int i = 0; !isPrime(n - i); i++) l++;
		printf("%d\n", l);
	}
	return 0;
}

这个比较暴力,比较慢,404ms

更快的方法是预处理存下所有素数,同时存下每个数在哪2个素数之间

代码:

#include<iostream>
#include<stdio.h>
using namespace std;
 
int list[1299710], p[100001];
 
int main()
{
	int key = 0, t, n;
	for (int i = 0; i < 1299710; i++)list[i] = i % 2;
	p[0] = 2, list[1] = 0, list[2] = 1;
	for (int i = 3; i < 1299710; i += 2)if (list[i])
	{
		p[++key] = i;
		for (int j = i + i; j < 1299710; j += i)list[j] = 0;
	}
	for (int i = 3; i < 1299710; i++)list[i] += list[i - 1];
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d", &n);
		printf("%d\n", p[list[n - 1]] - p[list[n] - 1]);
	}
	return 0;
}

这样就很快,52ms

CSU 2051 Num

题目:

Description
编一程序,输入正整数N(N在2~32767之间), 求它的最大质因子(包括它本身)。

Input
只有一行,就是正整数N

Output
所求的最大质因子

Sample Input
7
Sample Output
7

代码:

#include<iostream>
using namespace std;
 
bool prime(int n)
{
	if (n % 2 == 0)return n == 2;
	for (int i = 3; i*i <= n; i += 2)if (n%i==0)return false;
	return true;
}
 
int main()
{
	int n;
	cin >> n;
	for (int i = n;; i--)if (prime(i) && n%i == 0)
	{
		cout << i;
		break;
	}
	return 0;
}

力扣 2614. 对角线上的质数

给你一个下标从 0 开始的二维整数数组 nums 。

返回位于 nums 至少一条 对角线 上的最大 质数 。如果任一对角线上均不存在质数,返回 0 。

注意:

  • 如果某个整数大于 1 ,且不存在除 1 和自身之外的正整数因子,则认为该整数是一个质数。
  • 如果存在整数 i ,使得 nums[i][i] = val 或者 nums[i][nums.length - i - 1]= val ,则认为整数 val 位于 nums 的一条对角线上。

在上图中,一条对角线是 [1,5,9] ,而另一条对角线是 [3,5,7] 。

示例 1:

输入:nums = [[1,2,3],[5,6,7],[9,10,11]]
输出:11
解释:数字 1、3、6、9 和 11 是所有 "位于至少一条对角线上" 的数字。由于 11 是最大的质数,故返回 11 。

示例 2:

输入:nums = [[1,2,3],[5,17,7],[9,11,10]]
输出:17
解释:数字 1、3、9、10 和 17 是所有满足"位于至少一条对角线上"的数字。由于 17 是最大的质数,故返回 17 。

提示:

  • 1 <= nums.length <= 300
  • nums.length == numsi.length
  • 1 <= nums[i][j] <= 4*106
class Solution {
public:
	int diagonalPrime(vector<vector<int>>& nums) {
		int ans = 0;
		for (int i = 0; i < nums.size(); i++) {
			if (IsPrime(nums[i][i]))ans = max(ans, nums[i][i]);
			if (IsPrime(nums[i][nums.size()-1-i]))ans = max(ans, nums[i][nums.size() - 1 - i]);
		}
		return ans > 1 ? ans : 0;
	}
};

力扣 2601. 质数减法运算

给你一个下标从 0 开始的整数数组 nums ,数组长度为 n 。

你可以执行无限次下述运算:

  • 选择一个之前未选过的下标 i ,并选择一个 严格小于 nums[i] 的质数 p ,从 nums[i] 中减去 p 。

如果你能通过上述运算使得 nums 成为严格递增数组,则返回 true ;否则返回 false 。

严格递增数组 中的每个元素都严格大于其前面的元素。

示例 1:

输入:nums = [4,9,6,10]
输出:true
解释:
在第一次运算中:选择 i = 0 和 p = 3 ,然后从 nums[0] 减去 3 ,nums 变为 [1,9,6,10] 。
在第二次运算中:选择 i = 1 和 p = 7 ,然后从 nums[1] 减去 7 ,nums 变为 [1,2,6,10] 。
第二次运算后,nums 按严格递增顺序排序,因此答案为 true 。

示例 2:

输入:nums = [6,8,11,12]
输出:true
解释:nums 从一开始就按严格递增顺序排序,因此不需要执行任何运算。

示例 3:

输入:nums = [5,8,3]
输出:false
解释:可以证明,执行运算无法使 nums 按严格递增顺序排序,因此答案是 false 。

提示:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i] <= 1000
  • nums.length == n
class Solution {
public:
	bool primeSubOperation(vector<int>& nums) {
		auto vp = GetPrime(2000);
		nums.insert(nums.begin(), 0);
		for (int i = 1; i < nums.size(); i++) {
			int pmax = nums[i] - nums[i - 1] - 1;
			if (pmax < 0)return false;
			int p = 0;
			for (int j = 0; j < vp.size(); j++)if (vp[j] <= pmax)p = vp[j];
			nums[i] -= p;
		}
		return true;
	}
};

力扣 3115. 质数的最大距离

给你一个整数数组 nums

返回两个(不一定不同的)质数在 nums 中 下标 的 最大距离

示例 1:

输入: nums = [4,2,9,5,3]

输出: 3

解释: nums[1]nums[3] 和 nums[4] 是质数。因此答案是 |4 - 1| = 3

示例 2:

输入: nums = [4,8,2,8]

输出: 0

解释: nums[2] 是质数。因为只有一个质数,所以答案是 |2 - 2| = 0

提示:

  • 1 <= nums.length <= 3 * 105
  • 1 <= nums[i] <= 100
  • 输入保证 nums 中至少有一个质数。
class Solution {
public:
    int maximumPrimeDifference(vector<int>& nums) {
        int a=0,b=nums.size()-1;
        for(int i=0;i<nums.size();i++){
            if(nums[i] > 1 && IsPrime(nums[i]))a=max(a,i),b=min(b,i);
        }
        return a-b;
    }
};

二,因式分解

思路一:

分解n,只需要从1到根号n依次枚举,是否有n的约数即可。

时间复杂度:根号n

思路二:

预先把从1到根号n的素数全部拿到,只枚举素数。

主要耗时在于预先拿到所有素数耗时根号n,而只枚举素数的时间复杂度是低于根号n的。

总体时间复杂度:根号n

在需要多次调用因式分解算法时,拿到所有素数的操作只需要进行一次,所以均摊时间复杂度是低于根号n的。

//因式分解
	static vector<pair<int, int>> fenjie(int n)
	{
		vector<pair<int, int>> vp;
		for (auto i : Sieve::GetSingleA().getPrime(sqrt(n + 1)))
		{
			if (n % i)continue;
			int k = 0;
			while (n % i == 0)n /= i, k++;
			vp.push_back({ i, k });
		}
		if (n > 1) vp.push_back({ n, 1 });
		return vp;
	}

CSU 1522 Extravagant number

题目:

Description

An extravagant number (also known as a wasteful number) is a natural number which has fewer digits than the number of digits in its prime factorization (including exponents).For example, in base-10 arithmetic 4 = 2², 6 = 2 × 3, 8 = 2³, and 9 = 3² are extravagant numbers. Extravagant numbers can be defined in any base. There are infinitely many extravagant numbers, no matter what base is used.
----according to wikipedia.
Your task is to judge whether an integer (in base-10) is an extravagant number or not.

Input

There are several test cases.

First line of the input is the an integer T which means the number of test cases. Then Tlines follow, each line contains an integer n (n<=1000000).

Output

For each test case, you should output one line. If the number is an extravagant number, you should output “Yes”. Otherwise, “No”.

Sample Input

3
4
256
125
Sample Output

Yes
No
No


这个题目完全没什么技巧,直接求出因式分解,然后用一个函数num求出任意整数m有多少位。

要说唯一的技巧,那就是不需要任何数组。

代码:

#include<iostream>
using namespace std;

bool prime(int m)
{
	for (int i = 2; i*i <= m; i++)if (m%i == 0)return false;
	return true;
}

int num(int m)	
{
	if (m == 1)return 0;
	int s = 0;		
	while (m)
	{
		s++;
		m /= 10;
	}
	return s;
}

int main()
{
	int t;
	int n, k, sum;
	cin >> t;
	for (int cas = 1; cas <= t; cas++)
	{
		cin >> n;		
		sum = num(n);
		for (int i = 2; i <= n; i++)
		{
			if (i + i > n)i = n;
			if (prime(i) && n%i == 0)
			{
				k = 0;
				while (n%i == 0)
				{
					k++;
					n /= i;
				}
				sum -= num(i)+num(k);
			}
		}
		if (sum < 0)cout << "Yes";
		else cout << "No";
		cout << endl;
	}
	return 0;
}

CodeForces 27E 谁有N个因数?

题目:

Description

Given the number n, find the smallest positive integer which has exactly n divisors. It is guaranteed that for the given n the answer will not exceed 1018.

Input

The first line of the input contains integer n (1 ≤ n ≤ 1000).

Output

Output the smallest positive integer with exactly n divisors.

Sample Input

Input
4
Output
6
Input
6
Output
12

这个题目,我最大的失败就在于,考虑了很多不必要的细节。

不过反正比赛也结束了,现在终于也AC了,还是写一下吧。

首先,结果肯定只会出现2^a * 3^b * 5^c * 7^d * 11^e * 13^f ^ 15^g这种形式的数,其中a,b,c,d,e,f,g都是非负整数。

其次,a,b,c,d,e,f,g肯定是单调不增的。

于是可以进行一系列的估值(我好无聊)

得到,g<2,f<3,e<3,d<5,c<7,b要么不超过6,要么是某个素数-1,a要么是不超过8,要么是某个素数-1

而且,b本身不超过35,a本身不超过59。

于是还专门写了一个判断是不是10到60之间的素数的函数。

光是这几个取值范围,结果也就只有十几万种情况,如果再考虑单调性,应该只有几千种而已。


不过,这实在太细致了,没什么卵用。

代码:

#include<iostream>
using namespace std;
 
bool is_prime(int n)	//10到60的素数
{
	if (n == 11 || n == 13 || n == 17 || n == 19 || n == 23 || n == 29 || n == 31 || n == 37)return true;
	if (n == 41 || n == 43 || n == 47 || n == 53 || n == 59)return true;
	return false;
}
 
int main()
{
	int n;
	while (cin >> n)
	{
		long long min = 1000000000000000000;
		int m1 = n;
		for (int i17 = 0; i17 < 2; i17++)
		{
			if (m1 % (i17 + 1))continue;
			int m2 = m1 / (i17 + 1);
			for (int i13 = i17; i13 < 3; i13++)
			{
				if (m2 % (i13 + 1))continue;
				int m3 = m2 / (i13 + 1);
				for (int i11 = i13; i11 < 3; i11++)
				{
					if (m3 % (i11 + 1))continue;
					int m4 = m3 / (i11 + 1);
					for (int i7 = i11; i7 < 5; i7++)
					{
						if (m4 % (i7 + 1))continue;
						int m5 = m4 / (i7 + 1);
						for (int i5 = i7; i5 < 7; i5++)
						{
							if (m5 % (i5 + 1))continue;
							int m6 = m5 / (i5 + 1);
							for (int i3 = i5; i3 < 31; i3++)
							{
							if (i3>6 && !is_prime(i3 + 1))continue;
							if (m6 % (i3 + 1))continue;
							int i2 = m6 / (i3 + 1) - 1;
							if (i2 <i3)continue;
							if (i2>8 && !is_prime(i2 + 1))continue;
							long long s = 1;
							for (int i = 0; i < i17; i++)s *= 17;
							for (int i = 0; i < i13; i++)s *= 13;
							for (int i = 0; i < i11; i++)s *= 11;
							for (int i = 0; i < i7; i++)s *= 7;
							for (int i = 0; i < i5; i++)s *= 5;
							for (int i = 0; i < i3; i++)
							{
								s *= 3;
								if (s < 0)break;
							}
							if (s < 0)continue;
							for (int i = 0; i < i2; i++)
							{
								s *= 2;
								if (s < 0)break;
							}
							if (s<0)continue;
							if (min>s)min = s;
							}
						}
					}
				}
			}
		}
		cout << min;
		cout << endl;
	}
	return 0;
}

力扣 1998. 数组的最大公因数排序(集合并查集)

给你一个整数数组 nums ,你可以在 nums 上执行下述操作 任意次 :

如果 gcd(nums[i], nums[j]) > 1 ,交换 nums[i] 和 nums[j] 的位置。其中 gcd(nums[i], nums[j]) 是 nums[i] 和 nums[j] 的最大公因数。
如果能使用上述交换方式将 nums 按 非递减顺序 排列,返回 true ;否则,返回 false 。

示例 1:

输入:nums = [7,21,3]
输出:true
解释:可以执行下述操作完成对 [7,21,3] 的排序:
- 交换 7 和 21 因为 gcd(7,21) = 7 。nums = [21,7,3]
- 交换 21 和 3 因为 gcd(21,3) = 3 。nums = [3,7,21]
示例 2:

输入:nums = [5,2,6,2]
输出:false
解释:无法完成排序,因为 5 不能与其他元素交换。
示例 3:

输入:nums = [10,5,9,3,15]
输出:true
解释:
可以执行下述操作完成对 [10,5,9,3,15] 的排序:
- 交换 10 和 15 因为 gcd(10,15) = 5 。nums = [15,5,9,3,10]
- 交换 15 和 3 因为 gcd(15,3) = 3 。nums = [3,5,9,15,10]
- 交换 10 和 15 因为 gcd(10,15) = 5 。nums = [3,5,9,10,15]
 

提示:

1 <= nums.length <= 3 * 104
2 <= nums[i] <= 105

思路一:gcd+并查集

如果2个数的gcd>1,那么就给他们连一条边,最终用并查集分割成若干个子数组。

把所有子数组排序,看看是否满足最终条件即可。

时间复杂度10000^2超了。

思路二:筛法+并查集

先调用Sieve预算一下,不超过50000的素数有5133个。

枚举所有素数,对每个素数,遍历数组,把含有这个素数的那些数连起来。

这个思路的时间复杂度是10000*5133,还是超了。

思路三,因式分解+并查集

再调用Sieve预算一下,不超过1000的素数不到200个(这个百度一下也能查到),所以对1个数进行因式分解只需要200次运算。

遍历数组,把每个数做因式分解,动态维护并查集。

这个思路的时间复杂度是10000*200

class Solution {
public:
	bool gcdSort(vector<int>& nums) {
		vector<vector<int>>v;
		for (int i = 0; i < nums.size(); i++)v.push_back(GetFacs(nums[i]));
		Vunion<int> u(nums.size());
		u.push(v);
		map<int, multiset<int>>mnum;
		map<int, multiset<int>>mid;
		for (int i = 0; i < nums.size(); i++) {
			mnum[u.find(i)].emplace(nums[i]);
			mid[u.find(i)].emplace(i);
		}
		for (auto kv : mid) {
			auto &id = kv.second;
			auto &num = mnum[kv.first];
			for (auto it1 = id.begin(), it2 = num.begin(); it1 != id.end(); it1++, it2++)nums[*it1] = *it2;
		}
		for (int i = 1; i < nums.size(); i++)if (nums[i] < nums[i - 1])return false;
		return true;
	}
};

力扣 952. 按公因数计算最大组件大小(集合并查集)

给定一个由不同正整数的组成的非空数组 nums ,考虑下面的图:

  • 有 nums.length 个节点,按从 nums[0] 到 nums[nums.length - 1] 标记;
  • 只有当 nums[i] 和 nums[j] 共用一个大于 1 的公因数时,nums[i] 和 nums[j]之间才有一条边。

返回 图中最大连通组件的大小 。

示例 1:

输入:nums = [4,6,15,35]
输出:4

示例 2:

输入:nums = [20,50,9,63]
输出:2

示例 3:

输入:nums = [2,3,6,7,4,12,21,39]
输出:8

提示:

  • 1 <= nums.length <= 2 * 104
  • 1 <= nums[i] <= 105
  • nums 中所有值都 不同
class Solution {
public:
	int largestComponentSize(vector<int>& nums) {
		vector<vector<int>>v;
		for (int i = 0; i < nums.size(); i++)v.push_back(GetFacs(nums[i]));
		Vunion<int> u(nums.size());
		u.push(v);
		vector<vector<int>> g = u.getGroups();
		int ans = 0;
		for (auto &gi : g)ans = max(ans, int(gi.size()));
		return ans;
	}
};

力扣 2709. 最大公约数遍历(集合并查集)

给你一个下标从 0 开始的整数数组 nums ,你可以在一些下标之间遍历。对于两个下标 i 和 ji != j),当且仅当 gcd(nums[i], nums[j]) > 1 时,我们可以在两个下标之间通行,其中 gcd 是两个数的 最大公约数 。

你需要判断 nums 数组中 任意 两个满足 i < j 的下标 i 和 j ,是否存在若干次通行可以从 i 遍历到 j 。

如果任意满足条件的下标对都可以遍历,那么返回 true ,否则返回 false 。

示例 1:

输入:nums = [2,3,6]
输出:true
解释:这个例子中,总共有 3 个下标对:(0, 1) ,(0, 2) 和 (1, 2) 。
从下标 0 到下标 1 ,我们可以遍历 0 -> 2 -> 1 ,我们可以从下标 0 到 2 是因为 gcd(nums[0], nums[2]) = gcd(2, 6) = 2 > 1 ,从下标 2 到 1 是因为 gcd(nums[2], nums[1]) = gcd(6, 3) = 3 > 1 。
从下标 0 到下标 2 ,我们可以直接遍历,因为 gcd(nums[0], nums[2]) = gcd(2, 6) = 2 > 1 。同理,我们也可以从下标 1 到 2 因为 gcd(nums[1], nums[2]) = gcd(3, 6) = 3 > 1 。

示例 2:

输入:nums = [3,9,5]
输出:false
解释:我们没法从下标 0 到 2 ,所以返回 false 。

示例 3:

输入:nums = [4,3,12,8]
输出:true
解释:总共有 6 个下标对:(0, 1) ,(0, 2) ,(0, 3) ,(1, 2) ,(1, 3) 和 (2, 3) 。所有下标对之间都存在可行的遍历,所以返回 true 。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105
class Solution {
public:
	bool canTraverseAllPairs(vector<int>& nums) {
		vector<vector<int>>v;
		for (int i = 0; i < nums.size(); i++)v.push_back(GetFacs(nums[i]));
		Vunion<int> u(nums.size());
		u.push(v);
		return u.getRootNums() == 1;
	}
};

力扣 829. 连续整数求和

给定一个正整数 n,返回 连续正整数满足所有数字之和为 n 的组数 。 

例 1:

输入: n = 5
输出: 2
解释: 5 = 2 + 3,共有两组连续整数([5],[2,3])求和后为 5。

示例 2:

输入: n = 9
输出: 3
解释: 9 = 4 + 5 = 2 + 3 + 4

示例 3:

输入: n = 15
输出: 4
解释: 15 = 8 + 7 = 4 + 5 + 6 = 1 + 2 + 3 + 4 + 5

提示:

  • 1 <= n <= 109
class Solution {
public:
	int consecutiveNumbersSum(int n) {
		while (n %2  == 0)n /=2;
		return GetDivisors(n).size();
	}
};

力扣 1390. 四因数

给你一个整数数组 nums,请你返回该数组中恰有四个因数的这些整数的各因数之和。如果数组中不存在满足题意的整数,则返回 0 。

示例 1:

输入:nums = [21,4,7]
输出:32
解释:
21 有 4 个因数:1, 3, 7, 21
4 有 3 个因数:1, 2, 4
7 有 2 个因数:1, 7
答案仅为 21 的所有因数的和。

示例 2:

输入: nums = [21,21]
输出: 64

示例 3:

输入: nums = [1,2,3,4,5]
输出: 0

提示:

  • 1 <= nums.length <= 104
  • 1 <= nums[i] <= 105
class Solution {
public:
	int sumFourDivisors(vector<int>& nums) {
		int ans = 0;
		for (auto x : nums) {
			auto v = GetDivisors(x);
			if (v.size() == 4)ans += v[0] + v[1] + v[2] + v[3];
		}
		return ans;
	}
};

力扣 1492. n 的第 k 个因子

给你两个正整数 n 和 k 。

如果正整数 i 满足 n % i == 0 ,那么我们就说正整数 i 是整数 n 的因子。

考虑整数 n 的所有因子,将它们 升序排列 。请你返回第 k 个因子。如果 n 的因子数少于 k ,请你返回 -1 。

示例 1:

输入:n = 12, k = 3
输出:3
解释:因子列表包括 [1, 2, 3, 4, 6, 12],第 3 个因子是 3 。

示例 2:

输入:n = 7, k = 2
输出:7
解释:因子列表包括 [1, 7] ,第 2 个因子是 7 。

示例 3:

输入:n = 4, k = 4
输出:-1
解释:因子列表包括 [1, 2, 4] ,只有 3 个因子,所以我们应该返回 -1 。

提示:

  • 1 <= k <= n <= 1000

进阶:

你可以设计时间复杂度小于 O(n) 的算法来解决此问题吗?

class Solution {
public:
	int kthFactor(int n, int k) {
		auto v = GetDivisors(n);
		return k <= v.size() ? v[k-1] : -1;
	}
};

力扣 2427. 公因子的数目

给你两个正整数 a 和 b ,返回 a 和 b 的  因子的数目。

如果 x 可以同时整除 a 和 b ,则认为 x 是 a 和 b 的一个 公因子 。

示例 1:

输入:a = 12, b = 6
输出:4
解释:12 和 6 的公因子是 1、2、3、6 。

示例 2:

输入:a = 25, b = 30
输出:2
解释:25 和 30 的公因子是 1、5 。

提示:

  • 1 <= a, b <= 1000
class Solution {
public:
	int commonFactors(int a, int b) {
		return GetDivisors(Gcd(a, b)).size();
	}
};

力扣 2507. 使用质因数之和替换后可以取到的最小值

给你一个正整数 n 。

请你将 n 的值替换为 n 的 质因数 之和,重复这一过程。

  • 注意,如果 n 能够被某个质因数多次整除,则在求和时,应当包含这个质因数同样次数。

返回 n 可以取到的最小值。

示例 1:

输入:n = 15
输出:5
解释:最开始,n = 15 。
15 = 3 * 5 ,所以 n 替换为 3 + 5 = 8 。
8 = 2 * 2 * 2 ,所以 n 替换为 2 + 2 + 2 = 6 。
6 = 2 * 3 ,所以 n 替换为 2 + 3 = 5 。
5 是 n 可以取到的最小值。

示例 2:

输入:n = 3
输出:3
解释:最开始,n = 3 。
3 是 n 可以取到的最小值。

提示:

  • 2 <= n <= 105
class Solution {
public:
	int smallestValue(int n) {
		auto v = Fenjie(n);
		int x = 0;
		for (auto vi : v)x += vi.first*vi.second;
		if (x == n)return x;
		return smallestValue(x);
	}
};

力扣 2521. 数组乘积中的不同质因数数目

给你一个正整数数组 nums ,对 nums 所有元素求积之后,找出并返回乘积中 不同质因数 的数目。

注意:

  • 质数 是指大于 1 且仅能被 1 及自身整除的数字。
  • 如果 val2 / val1 是一个整数,则整数 val1 是另一个整数 val2 的一个因数。

示例 1:

输入:nums = [2,4,3,7,10,6]
输出:4
解释:
nums 中所有元素的乘积是:2 * 4 * 3 * 7 * 10 * 6 = 10080 = 25 * 32 * 5 * 7 。
共有 4 个不同的质因数,所以返回 4 。

示例 2:

输入:nums = [2,4,8,16]
输出:1
解释:
nums 中所有元素的乘积是:2 * 4 * 8 * 16 = 1024 = 210 。
共有 1 个不同的质因数,所以返回 1 。

提示:

  • 1 <= nums.length <= 104
  • 2 <= nums[i] <= 1000
class Solution {
public:
	int distinctPrimeFactors(vector<int>& nums) {
		map<int, int>m;
		for (auto x : nums) {
			auto v = GetFacs(x);
			for (auto x : v) {
				m[x]++;
			}
		}
		return m.size();
	}
};

力扣 2183. 统计可以被 K 整除的下标对数目

给你一个下标从 0 开始、长度为 n 的整数数组 nums 和一个整数 k ,返回满足下述条件的下标对 (i, j) 的数目:

  • 0 <= i < j <= n - 1 且
  • nums[i] * nums[j] 能被 k 整除。

示例 1:

输入:nums = [1,2,3,4,5], k = 2
输出:7
解释:
共有 7 对下标的对应积可以被 2 整除:
(0, 1)、(0, 3)、(1, 2)、(1, 3)、(1, 4)、(2, 3) 和 (3, 4)
它们的积分别是 2、4、6、8、10、12 和 20 。
其他下标对,例如 (0, 2) 和 (2, 4) 的乘积分别是 3 和 15 ,都无法被 2 整除。    

示例 2:

输入:nums = [1,2,3,4], k = 5
输出:0
解释:不存在对应积可以被 5 整除的下标对。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i], k <= 105
class Solution {
public:
	long long countPairs(vector<int>& nums, int k) {
		map<int, int>m;
		long long ans = 0;
		for (auto x : nums) {
			auto g = Gcd(x, k);
			auto v = GetDivisors(g);
			ans += m[k / g];
			for (auto vi : v) m[vi]++;
		}
		return ans;
	}
};

力扣 2584. 分割数组使乘积互质

给你一个长度为 n 的整数数组 nums ,下标从 0 开始。

如果在下标 i 处 分割 数组,其中 0 <= i <= n - 2 ,使前 i + 1 个元素的乘积和剩余元素的乘积互质,则认为该分割 有效 。

  • 例如,如果 nums = [2, 3, 3] ,那么在下标 i = 0 处的分割有效,因为 2 和 9 互质,而在下标 i = 1 处的分割无效,因为 6 和 3 不互质。在下标 i = 2 处的分割也无效,因为 i == n - 1 。

返回可以有效分割数组的最小下标 i ,如果不存在有效分割,则返回 -1 。

当且仅当 gcd(val1, val2) == 1 成立时,val1 和 val2 这两个值才是互质的,其中 gcd(val1, val2) 表示 val1 和 val2 的最大公约数。

示例 1:

输入:nums = [4,7,8,15,3,5]
输出:2
解释:上表展示了每个下标 i 处的前 i + 1 个元素的乘积、剩余元素的乘积和它们的最大公约数的值。
唯一一个有效分割位于下标 2 。

示例 2:

输入:nums = [4,7,15,8,3,5]
输出:-1
解释:上表展示了每个下标 i 处的前 i + 1 个元素的乘积、剩余元素的乘积和它们的最大公约数的值。
不存在有效分割。

提示:

  • n == nums.length
  • 1 <= n <= 104
  • 1 <= nums[i] <= 106
class Solution {
public:
	int findValidSplit(vector<int>& nums) {
		auto v1 = pnum(nums);
		auto v2= pnum(Frev(nums));
		for (int i = 0; i < nums.size() - 1; i++) {
			if (v1[i] + v2[nums.size() - 2 - i] == v1[nums.size() - 1])return i;
		}
		return -1;
	}
	vector<int> pnum(const vector<int>& nums)
	{
		map<int, int>m;
		vector<int>ans;
		for (auto x : nums) {
			auto v = GetFacs(x);
			for (auto vi : v)m[vi]++;
			ans.push_back(m.size());
		}
		return ans;
	}
};

力扣 2818. 操作使得分最大

给你一个长度为 n 的正整数数组 nums 和一个整数 k 。

一开始,你的分数为 1 。你可以进行以下操作至多 k 次,目标是使你的分数最大:

  • 选择一个之前没有选过的 非空 子数组 nums[l, ..., r] 。
  • 从 nums[l, ..., r] 里面选择一个 质数分数 最高的元素 x 。如果多个元素质数分数相同且最高,选择下标最小的一个。
  • 将你的分数乘以 x 。

nums[l, ..., r] 表示 nums 中起始下标为 l ,结束下标为 r 的子数组,两个端点都包含。

一个整数的 质数分数 等于 x 不同质因子的数目。比方说, 300 的质数分数为 3 ,因为 300 = 2 * 2 * 3 * 5 * 5 。

请你返回进行至多 k 次操作后,可以得到的 最大分数 。

由于答案可能很大,请你将结果对 109 + 7 取余后返回。

示例 1:

输入:nums = [8,3,9,3,8], k = 2
输出:81
解释:进行以下操作可以得到分数 81 :
- 选择子数组 nums[2, ..., 2] 。nums[2] 是子数组中唯一的元素。所以我们将分数乘以 nums[2] ,分数变为 1 * 9 = 9 。
- 选择子数组 nums[2, ..., 3] 。nums[2] 和 nums[3] 质数分数都为 1 ,但是 nums[2] 下标更小。所以我们将分数乘以 nums[2] ,分数变为 9 * 9 = 81 。
81 是可以得到的最高得分。

示例 2:

输入:nums = [19,12,14,6,10,18], k = 3
输出:4788
解释:进行以下操作可以得到分数 4788 :
- 选择子数组 nums[0, ..., 0] 。nums[0] 是子数组中唯一的元素。所以我们将分数乘以 nums[0] ,分数变为 1 * 19 = 19 。
- 选择子数组 nums[5, ..., 5] 。nums[5] 是子数组中唯一的元素。所以我们将分数乘以 nums[5] ,分数变为 19 * 18 = 342 。
- 选择子数组 nums[2, ..., 3] 。nums[2] 和 nums[3] 质数分数都为 2,但是 nums[2] 下标更小。所以我们将分数乘以 nums[2] ,分数变为  342 * 14 = 4788 。
4788 是可以得到的最高的分。

提示:

  • 1 <= nums.length == n <= 105
  • 1 <= nums[i] <= 105
  • 1 <= k <= min(n * (n + 1) / 2, 109)
class Solution {
public:
	int maximumScore(vector<int>& nums, int k) {
		vector<int>v;
		for (auto x : nums)v.push_back(GetFacs(x).size());
		auto left = Fmaxlef2(v);
		auto right = Fmaxrig(v);
		vector<long long>v2;
		for (long long i = 0; i < v.size(); i++) {
			v2.push_back((i - left[i])*(right[i] - i));
		}
		auto id = SortId(nums);
		long long ans = 1;
		for (int i = id.size() - 1; k && i >= 0; i--) {
			long long m = min((long long)k, v2[id[i]]);
			ans = ans * MultiMulti(nums[id[i]], m, 1000000007) % 1000000007;
			k -= m;
		}
		return ans;
	}
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值