1 条题解

  • 0
    @ 2026-2-18 15:52:33
    #include<bits/stdc++.h>
    using namespace std;
    
    // [1] 全局常量:最大数值限制,适配题目B≤100000的范围
    const int MAXN = 100010;
    
    // [2] 全局数组:并查集父节点数组,存储每个整数的父节点编号
    int fa[MAXN];
    // [3] 全局数组:并查集深度数组,用于按秩合并优化
    int dep[MAXN];
    // [4] 全局数组:埃氏筛标记数组,标记每个数是否为质数
    bool is_prime[MAXN];
    // [5] 全局数组:存储筛出的所有质数
    vector<int> primes;
    // [6] 全局数组:标记根节点是否已被统计,避免栈溢出
    bool vis[MAXN];
    
    // 函数声明
    void sieve(int maxn);
    int find(int x);
    void unite(int x, int y);
    
    // [7] 主函数:程序运行入口
    int main()
    {
        int A, B, P;
        cin >> A >> B >> P; // [8] 读入整数范围A、B,以及公共质因数阈值P
        
        // [9] 调用sieve函数筛出MAXN以内的所有质数,跳转至该函数完成注释
        sieve(MAXN - 1);
        
        // [17] 回到主函数,筛质数完成
        // [18] 循环初始化并查集,为每个整数初始化集合状态
        for(int i = 0; i < MAXN; i++)
        {
            fa[i] = i; // [19] 每个整数初始独立为一个集合,父节点设为自身
            dep[i] = 1; // [20] 每个集合的初始树深度设为1
            vis[i] = false; // [21] 初始化标记数组为false
        }
        
        // [22] 遍历所有筛出的质数,处理符合条件的质数
        for(int p : primes)
        {
            if(p < P || p > B) continue; // [23] 跳过小于P或大于B的质数,不满足合并条件
            
            // [24] 计算当前质数p在[A,B]范围内的第一个倍数
            int first = ((A + p - 1) / p) * p; // 向上取整计算第一个>=A的p的倍数
            if(first > B) continue; // [25] 范围内无该质数的倍数,跳过
            
            // [26] 循环遍历该质数的所有倍数,合并到同一个集合
            for(int j = first + p; j <= B; j += p)
            {
                // [27] 调用unite函数合并当前倍数和第一个倍数,跳转至该函数完成注释
                unite(first, j);
            }
        }
        
        // [39] 回到主函数,所有合并操作完成
        int setCnt = 0; // [40] 统计变量:记录最终的集合数量
        // [41] 循环遍历[A,B]范围内的所有整数,统计集合数量
        for(int i = A; i <= B; i++)
        {
            // [42] 调用已注释的find函数,获取当前整数的根节点
            int root = find(i);
            if(!vis[root]) // [43] 根节点未被统计过
            {
                setCnt++; // [44] 集合计数+1
                vis[root] = true; // [45] 标记该根节点已统计
            }
        }
        
        cout << setCnt << endl; // [46] 输出最终的集合数量
        return 0; // [47] 主函数正常结束
    }
    
    // [10] 跳转至sieve函数:埃氏筛法,筛出maxn以内的所有质数,修复溢出问题
    void sieve(int maxn)
    {
        // [11] 函数参数maxn:筛数的上限
        memset(is_prime, true, sizeof is_prime); // [12] 初始化所有数标记为质数
        is_prime[0] = is_prime[1] = false; // [13] 0和1不是质数,标记为false
        
        // [14] 循环筛除合数,修复整数溢出问题
        for(int i = 2; i <= maxn; i++)
        {
            if(is_prime[i]) // [15] 若当前数是质数
            {
                primes.push_back(i); // 加入质数列表
                // 修复:j从i*2开始,彻底避免i*i溢出问题
                for(int j = i * 2; j <= maxn; j += i)
                {
                    is_prime[j] = false;
                }
            }
        }
    }
    // [16] sieve函数注释完成,返回主函数继续注释
    
    // [28] 跳转至find函数:带路径压缩,查找节点x所在集合的根节点
    int find(int x)
    {
        // [29] 函数参数x:待查找根节点的整数
        if(x != fa[x])
        {
            fa[x] = find(fa[x]); // [30] 路径压缩:直接将当前节点指向根节点,优化查询效率
        }
        return fa[x]; // [31] 返回找到的根节点
    }
    // [32] find函数注释完成,返回unite函数继续注释
    
    // [33] 跳转至unite函数:按秩合并x和y所在的两个集合
    void unite(int x, int y)
    {
        // [34] 函数参数x、y:待合并的两个整数
        x = find(x); // [35] 查找x的根节点
        y = find(y); // [36] 查找y的根节点
        
        if(x == y) return; // [37] 两数已属于同一集合,无需重复合并
        
        // [38] 按秩合并逻辑:将深度更小的树合并到深度更大的树下,保持树结构平衡
        if(dep[x] > dep[y])
        {
            fa[y] = x;
        }
        else if(dep[x] < dep[y])
        {
            fa[x] = y;
        }
        else
        {
            fa[y] = x;
            dep[x]++; // 两棵树深度相同时,合并后根节点深度+1
        }
    }
    // [39] unite函数注释完成,返回主函数继续注释
    
    • 1

    信息

    ID
    1366
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    3
    已通过
    1
    上传者