数论-筛法思想题目合辑

筛法思想题目合辑

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 ai2e5 ,保证序列中每个数两两不同

例如:

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;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爪蛙毁一生

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

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

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

打赏作者

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

抵扣说明:

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

余额充值