2022牛客寒假算法基础集训营4 &J

         本篇来自于题解之后,本来是直接打算暴力枚举l ,r 内的合数来 lcm(a,b),却犯了一个致命的错误用了lcm(a%mod,b)导致直接wa掉。(lcm(a,b) !=lcm(a%mod,b)

赛后ak的hrgg讲完才知道用的唯一分解定理。

用兰子大佬赛后题解举例:

作者:比那名居的桃子
链接:【题解】2022年第四场寒假集训营题解_ACM竞赛_ACM/CSP/ICPC/CCPC/比赛经验/题解/资讯_牛客竞赛OJ_牛客网

这个题可以转化为:需要统计区间(l~r)内每个素数出现的最高的幂。

这里再解释一下这句话的含义。例如:

a=2^3∗3^5∗5^1

b=2^4∗3^1∗5^2,

那么它们的最小公倍数就是

c = 2^4∗3^5∗5^2,也就是每个素数的幂取最大值。

所以我们的思路也就清晰了,本题范围也不大,可以直接用线性筛筛出,对其作为合数因子的素数找出范围内的幂取最大值相乘即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll l,r;
const int N = 30050;
const int mod = 1000000007;
bool vis[N];
int prime[N],tot;
ll ans = 1;
//unordered_set<int>S;
void oula(int n)
{
    for(int i = 2; i <= n; i ++)
    {
        if(!vis[i]) prime[tot ++] = i;
        for(int j = 0; j < tot; j ++)
        {
            if(i * prime[j] > n)break;
            vis[i * prime[j]] = true;
            if(i %prime[j] == 0) break;
        }
    }
}
int main()
{
    scanf("%lld%lld",&l,&r);
    oula(N);//筛素数
    // for(int i = 0; i < tot; i ++)
    // {
    //      S.insert(prime[i]);
    // }
    int i , j;
    for(i = 0; prime[i] * 2 <= r; i ++)//据题意,本题指出是合数的lcm,因此我们这里进行一个x2的操作,来只挑出作为因子的素数,防止出错
    {
        for( j = prime[i]; j <= r; j *= prime[i])//此处枚举
        {
            if(r / j == (l - 1) / j) break;//此处判断该素数的倍数是否越界,如果越界则停止。
        }
        ans *= j / prime[i];//上个循环多乘了一倍超出界,÷回来
        ans %= mod;//取余
    }
    if(ans != 1)    cout<<ans<<endl;
    else cout<<-1<<endl;
    system("pause");
    return 0;
}
//本题题解改自兰子大佬的题解版

     注释补充:

1、prime[i] * 2处,由于是合数的lcm,因此我们×最小质数2,使得prime[i]能在全部满足作为因子的要求。例如当 r == 100 时,如果是prime[i] <= r 时,取得素数97,最终会ans *= 97^2/97,答案错误。

2、if(r / j == (l - 1) / j) break;处判断是否越界,表示区间[l,r]内不存在j的倍数,必须break;

r / j 表示[1,r]prime[i] 倍数的个数,(l - 1) / j 表示[1,l - 1] 内prime[i] 倍数的个数,

假设当r = 20,l = 5时,当prime[i] == 7 时,当7^2 = 49,上述等式均为0,即在区间[l,r]内均无该数,跳出,如果不跳出则为res *= 7,错误,应为res *= 1;

有问题欢迎指出~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值