筛法思想题目合辑
1.1求解 [ 1 , n ] [1,n] [1,n] 内每个数的约数的个数的和, n = 1 e 6 n = 1e6 n=1e6
例如:
n = 4 n=4 n=4
d ( 1 ) = 1 , d ( 2 ) = 2 , d ( 3 ) = 2 , d ( 4 ) = 3 , 1 + 2 + 2 + 3 = 8 d(1)=1,d(2)=2,d(3)=2,d(4)=3 , 1+2+2+3=8 d(1)=1,d(2)=2,d(3)=2,d(4)=3,1+2+2+3=8
思路:
1 2 3 4 都是1的倍数所以 n/1 等于所有数的约数含有1的个数
2 4 都是2的倍数所以 n/2 等于所有数的约数含有2的个数
所以可以推出 1-n 的约数之和就是
s
u
m
=
(
n
/
1
+
n
/
2...
n
/
n
)
sum = (n/1+n/2...n/n)
sum=(n/1+n/2...n/n)
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n , sum = 0;
cin >> n;
for(int i = 1 ; i <= n ; i++){
sum += n / i -1;
}
cout << sum << endl;
return 0;
}
1.2 求解 [ 1 , n ] [1,n] [1,n] 内有多少对数 i , j i,j i,j 满足 i i i 是 j j j的约数, n = 1 e 6 n = 1e6 n=1e6
例如:
n = 5 , a n s = 5 n = 5 , ans = 5 n=5,ans=5
n = 6 , a n s = 8 n=6,ans=8 n=6,ans=8
思路:
n = 5 时 有
1 2
1 3
1 4
1 5
2 4
可以推出
i
i
i 的倍数为
j
j
j 就是满足题目的要求的对数
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n , sum = 0;
cin >> n;
for(int i = 1 ; i <= n ; i++){
for(int j = i + i ; j <= n ;j += i){//累加除了自身以外 所有n以内的i的倍数
sum++;
}
}
cout << sum << endl;
return 0;
}
1.3 给定一个序列 a i a_i ai , 有多少对数 a i , a j a_i,a_j ai,aj 成倍数。输出对数,
序列长度 n = 2 e 5 n = 2e5 n=2e5, a i ≤ 2 e 5 a_i \leq 2e5 ai≤2e5 ,保证序列中每个数两两不同
例如:
n = 4 , a = [ 1 , 3 , 6 , 2 ] n = 4 , a = [1,3,6,2] n=4,a=[1,3,6,2]
输出: 5 5 5
思路:
做法1:
预处理出每一个数的约数,再从所有约数中去寻找与数组相等的约数
做法2:
记录是否有这个数,再从1-n中所有约数中累加有这个数的情况
代码:
做法1:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
vector<int> d[maxn];
int a[maxn];
int main()
{
for (int i = 1 ; i < maxn ; i++){ //预处理出每一个数的所有约数
for (int j = i ; j < maxn ; j += i){
d[j].push_back(i);
}
}
int n;
cin >> n;
set<int> s;
for (int i = 1 ; i <= n ; i++) {
cin >> a[i];
s.insert(a[i]); // 记录数组的数
}
int ans = 0;
for (int i = 1 ; i <= n ; i++){
for (auto x : d[a[i]]){//遍历a[i]的约数
if (s.count(x)) ans++; // 寻找数组内是否有这个约数
}
}
return 0;
}
做法2:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
int a[maxn],bk[maxn];
int main() {
int n;
cin >> n;
for(int i = 1 ; i <= n ;i++){
cin >> a[i];
bk[a[i]] = 1; //记录是否有这个数
}
int ans = 0;
for(int i = 1; i < maxn ; i++){//筛选法寻找每个数的倍数(相当于寻找每个数的所有约数)
for(int j = i + i ; j < maxn ; j += i){
ans += bk[j]; //在i倍数中 如果有这个数就累加
}
}
cout << ans <<endl;
return 0;
}
如果有重复,组合一下
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
int a[maxn],bk[maxn];
int main() {
int n;
cin >> n;
for(int i = 1 ; i <= n ;i++){
cin >> a[i];
bk[a[i]]++; //记录有几个这个数
}
int ans = 0;
for(int i = 1; i < maxn ; i++){//筛选法寻找每个数的倍数(相当于寻找每个数的所有约数)
ans += bk[i] * (bk[i] - 1) / 2; //排列组合
for(int j = i + i ; j < maxn ; j += i){
ans += bk[i] * bk[j]; //在i倍数中 如果有这个数就累加
}
}
cout << ans <<endl;
return 0;
1.4.除数博弈: 当 n = 2 e 5 n = 2e5 n=2e5 怎么做?
题目:
思路:
预处理出每一个数的约数,然后遍历约数,再使用之前的dp即可
代码:
##include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
bool dp[maxn];
vector<int> d[maxn];
int main() {
int n;
cin >> n;
for (int i = 1 ; i < maxn ; i++) {
for (int j = i + i ; j < maxn ; j += i) {
d[j].push_back(i);
}
}
for(int i = 1 ; i <= n ; i++) {
for(auto & x : d[i]) {
if(dp[i-x]==0)
dp[i]=1;
}
}
cout << dp[n];
return 0;
}
1.5 输出 [ 1 , n ] [1,n] [1,n]每个数质因分解后的最小质因子,最大质因子以及不同质因子的个数 , n = 1 e 6 n = 1e6 n=1e6
思路:
使用分解质因子思路,再开三个数组分别存储每个数 最小质因子,最大质因子以及不同质因子的个数
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
int mins[maxn]; // mins[i] 代表i的最小质因子
int maxs[maxn]; // maxs[i] 代表i的最大质因子
int counts[maxn];// counts[i] 代表i的不同的质因子个数
int main() {
int n;
cin >> n;
for(int i = 2 ; i <= n ; i++) {
if(mins[i] == 0) {
//i是质数 最大 最小都默认为自身
mins[i] = i;
maxs[i] = i;
counts[i] = 1;
for(int j = i + i ; j <= n ; j += i) {
//最小值 不覆盖
if(!mins[j]) mins[j] = i;
//最大值 每次覆盖
maxs[j] = i;
//个数 每次++
counts[j]++;
}
}
}
for (int i = 1 ; i <= n ; i++) {
cout <<i <<" : " << mins[i] << " " << maxs[i] << " " << counts[i] << endl;
}
return 0;
}