本篇来自于题解之后,本来是直接打算暴力枚举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;
有问题欢迎指出~