P6659 神仙lcm题
题意
给定一个数字 $n$,询问下标最小的区间 $[l,r]$ 使得 $\operatorname{lcm}(l,l+1,\dots,r)=n$。
此处下标较小定义为$$ \begin{cases}r\lt r^{\prime},&l=l^{\prime}\\l \lt l^{\prime},& \text{otherwise.} \end{cases} $$$n \leq 10^{18},t \leq 10^4$。
Solution
考虑区间长度为 $1$ 的情况,若 $n$ 为奇数,则必然存在区间 $[\frac{n-1}{2},\frac{n-1}{2}+1]$ 小于区间 $[n,n]$ 使得要求成立。若 $n$ 为偶数,则必然存在区间 $[2,\frac{n}{2}]$ 小于区间 $[n,n]$ 使得要求成立。故答案区间长度不可能为 $1$。
考虑区间长度为 $2$ 的情况,显然地,$l$ 与 $r$ 即 $l+1$ 互质,故 $\operatorname{lcm}(l,r)=l\times(l+1)$ 。可以证明,对于 $n=k\times (k+1),k\in N$,答案区间必然为 $[k,k+1]$。显然地,对于 $n \leq 10^{18},k\leq \sqrt{10^{18}}$,且单调递增,可以二分来找到答案。
考虑区间长度大于等于 $3$ 的情况,发现对于极端数据 $n$ 为 $10^{18}$ 级别时,答案区间内的数不会超过 $10^6$ ,故对于 $10^6$ 以下的左端点暴力枚举以及右端点,并使用 map 维护答案。
时间复杂度为 $\mathcal{O}(T \log n)$ 再加上预处理大概是 $\mathcal{O}(4 \times 10^6)$ 的巨大常数,可以通过本题。
code
#include<bits/stdc++.h>
using namespace std;
inline long long read()
{
long long res=0,flag=1;
char ch=getchar();
while(!isalnum(ch)) (ch=='-')?flag=-1:1,ch=getchar();
while(isalnum(ch)) res=res*10+ch-'0',ch=getchar();
return res*flag;
}
map<long long,pair<int,int> > ans;
long long gcd(long long a,long long b)
{
if(b==0)
return a;
return gcd(b,a%b);
}
void init()
{
for(int l=1;l<=1500000;l++)
{
long long tmp=1ll*l*(l+1);
for(long long r=l+2; ;r++)
{
tmp/=gcd(tmp,r);
if(tmp>1e18/r)
break;
tmp*=r;
if(ans.count(tmp)==false)
ans[tmp]=make_pair(l,r);
}
}
return ;
}
void solve(long long k)
{
long long left=1,right=1e9;
while(left<=right)
{
long long mid=(left+right)>>1;
if(mid*(mid+1)==k)
{
ans[k]=make_pair(mid,mid+1);
return ;
}
else if(mid*(mid+1)>k)
right=mid-1;
else if(mid*(mid+1)<k)
left=mid+1;
}
return ;
}
int main(int argc,const char *argv[])
{
init();
int T=read();
while(T--)
{
long long n=read();
if(ans.count(n)==true)
{
printf("%d %d\n",ans[n].first,ans[n].second);
continue;
}
solve(n);
if(ans.count(n)==true)
{
printf("%d %d\n",ans[n].first,ans[n].second);
continue;
}
printf("NIE\n");
}
return 0;
}
写在最后
三年 OI 一场空,不开 long long 见祖宗。

浙公网安备 33010602011771号