题意简述
给定 a , b ( a , b < = 1 e 9 ) a,b(a,b<=1e9) a,b(a,b<=1e9),和 T ( T < = 1 e 4 ) T(T<=1e4) T(T<=1e4)个询问,每次询问包含两个数 l , r ( l < = r < = 1 e 9 ) l,r(l<=r<=1e9) l,r(l<=r<=1e9),求 [ l , r ] [l,r] [l,r]中最大的 a , b a,b a,b的公因数。如果 l , r l,r l,r中没有一个数是 a , b a,b a,b的公因数,输出 − 1 -1 −1。
数据
输入
第一行给定两个正整数
a
,
b
a,b
a,b
第二行一个正整数
T
T
T
接下来
T
T
T行,每行两个正整数,表示一个询问
l
,
r
l,r
l,r。
输出
对于每个询问,输出这个询问的答案。
样例
输入
9 27
3
1 5
10 11
9 11
输出
3
-1
9
解释
对于第一个询问,输出
3
3
3。当然,
9
9
9和
27
27
27的公因数还有
9
9
9,但是
9
9
9不在给定的区间内。
对于第二个询问,区间中包含两个数
10
,
11
10,11
10,11,均不是
9
,
27
9,27
9,27的公因数。
对于第三个询问,区间包含三个数
9
,
10
,
11
9,10,11
9,10,11,其中满足是公因数且最大的是
9
9
9。
思路
刚看到这个题,我们可能有这样的思路:
枚举区间,判断是否是
a
,
b
a,b
a,b的公因数,找最大的那个。
但是,我们会发现,我们珂能多枚举了很多!因为区间中珂能有很多数不是
a
,
b
a,b
a,b的公因数。要优化掉这些多余的枚举,就要 开O2 想办法。一会,我们可能会想到,我们珂不珂以枚举
a
,
b
a,b
a,b的公因数,然后判断是否在区间内,求最大呢?
此时我们联想到一个定理:
a
,
b
a,b
a,b的所有公因数都是
g
c
d
(
a
,
b
)
gcd(a,b)
gcd(a,b)的因数。
还有另外一个定理:一个数
n
n
n的因数关于
n
\sqrt{n}
n对称(即两两乘积相等)。
根据这两个定理,我们就珂以把
g
c
d
(
a
,
b
)
gcd(a,b)
gcd(a,b)先求出来,然后打个表,排一下序,对于每次询问,二分一下就珂以了。那么,打表(也就是预处理)会不会爆空间呢?由第二个定理可知,
g
c
d
(
a
,
b
)
gcd(a,b)
gcd(a,b)的因数个数不会超过
2
g
c
d
(
a
,
b
)
2\sqrt{gcd(a,b)}
2gcd(a,b)。而
g
c
d
(
a
,
b
)
gcd(a,b)
gcd(a,b)有不会超过
m
i
n
(
a
,
b
)
min(a,b)
min(a,b),所以空间战友不会超过
1
e
9
\sqrt{1e9}
1e9,一定能存下。
具体如何二分呢?只要 u p p e r _ b o u n d upper\_bound upper_bound一下即可。 u p p e r _ b o u n d ( l , r , x ) upper\_bound(l,r,x) upper_bound(l,r,x)会返回指针区间 [ l , r ) [l,r) [l,r)内第一个大于 x x x的位置。那么,我们把这个位置 − 1 -1 −1就是最后一个 < = x <=x <=x的位置了。当然,我们在减去数组头,就珂以得到这个是数组的第几个了。
代码
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define int long long
#define N 100100
int a,b;
int d[N];//d保存gcd(a,b)的因数
void Build()
{
int g=__gcd(a,b);//求出gcd(a,b)
int& cnt=d[0];//为了节省空间,用d的第一个位置作为计数器
for(int i=1;i*i<=g;++i)//枚举到根号即珂
{
if (g%i==0)
{
d[++cnt]=i;
if (i*i!=g)//避免重复枚举
{
d[++cnt]=g/i;
}
}
}
sort(d+1,d+cnt+1);//排序,方便二分
}
void Query()
{
int T;scanf("%lld",&T);
while(T--)
{
int l,r;
scanf("%lld%lld",&l,&r);
int k=(upper_bound(d+1,d+d[0]+1,r)-1)-d;
//上面说过这里
if (d[k]>=l)//还要判一下是否>l
{
printf("%lld\n",d[k]);
}
else
{
puts("-1");
}
}
}
void Main()
{
scanf("%lld%lld",&a,&b);
Build();
Query();
}
};
main()
{
Flandle_Scarlet::Main();
return 0;
}
本文介绍了一个编程问题的解决方案,该问题要求在给定区间[l, r]内找到a, b的最大公因数。通过利用数学定理,可以先计算gcd(a, b),然后对gcd的因数进行排序,对每个询问使用二分查找来优化效率。这种方法避免了不必要的枚举,确保了空间和时间复杂度的合理性。"
42419479,4847039,K-means聚类算法详解与应用,"['机器学习', '数据挖掘', '无监督学习', '聚类算法', 'K-means']
352

被折叠的 条评论
为什么被折叠?



