目录
一,素数检测
素数检测就是给定一个正整数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
和 j
(i != 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;
}
};