《算法竞赛·快冲300题》将于2024年出版,是《算法竞赛》的辅助练习册。
所有题目放在自建的OJ New Online Judge。
用C/C++、Java、Python三种语言给出代码,以中低档题为主,适合入门、进阶。
【题目描述】 给定一个数字n,请问是否存在一个区间l,r,使得n等于整个区间所有数字的最小公倍数。
【输入格式】 第一行为正整数T,表示存在T组测试数据,1≤T≤10000。
对于每组测试数据,输入一个整数表示数字n,1≤n≤10^18。
【输出格式】 对于每组测试数据,如果存在区间[l,r]为答案,则输出两个数字l和r。
如果存在多组解,输出l最小的解。若仍存在多解,l相同,则输出r最小的解。
如果无解输出-1。
【输入样例】
3
12
504
17
【输出样例】
1 4
6 9
-1
如果直接计算n所对应的[L, R],只能暴力查找所有可能组合,计算量极大。容易想到一个简单一点的办法:反过来算,先预计算出所有[L,R]对应的n,然后对输入的n,查询它对应的[L, R]。
但是直接遍历所有的[L, R],计算量仍然很大。L和R可能的最大值是 n ≤ 1 0 18 = 1 0 9 \sqrt{n}≤ \sqrt{10^{18}}=10^9 n≤1018=109。遍历所有的L、R,计算量 O ( n n ) = O ( n ) O(\sqrt{n}\sqrt{n}) = O(n) O(nn)=O(n),超时。
如果[L, R]中至少有3个数,即至少包含[L, L+1, L+2],那么L的最大值是 n 3 ≤ 1 0 6 \sqrt[3]{n} ≤ 10^6 3n≤106,计算量减少很多。
至于只包含2个数的区间[L, L+1],可以单独检查,计算量很小。输入一个n值,检查 n n + 1 = n \sqrt{n}\sqrt{n+1}=n nn+1=n是否成立即可。
【重点】 GCD 。
用map存储计算结果,n对应的答案是第一次存到map中的[L, R]。
#include
using namespace std;
typedef long long ll;
const ll INF = 1e18;
map<ll, pair<int,int>>ans;
void init(){ //预处理,ans[n]表示n对应的答案[L,R],区间内至少3个数
for(ll L = 1; L <= 2000000; L++) {
ll n = L * (L + 1);
for(ll R = L + 2; ;R++) {
ll g = __gcd(n, R);
if(n / g > INF / R) break;
n = n / g * R; //先除再乘,防止溢出
if(!ans.count(n)) //res这个数还没有算过,存起来
ans[n] = make_pair(L, R);
}
}
}
int main(){
init();
int T; scanf("%d",&T);
while(T--) {
ll n; scanf("%lld",&n);
//先特判区间长度为2的情况: [L,L+1]
ll sqrt_n = sqrt(n + 0.5);
pair<int,int>res;
if(sqrt_n * (sqrt_n + 1) == n) {
res = make_pair(sqrt_n, sqrt_n + 1);
if(ans.count(n))
if(res.first > ans[n].first)
res = ans[n];
}
else if(ans.count(n)) res = ans[n];
else { puts("-1"); continue; }
printf("%d %d\n", res.first, res.second);
}
return 0;
}
import java.util.*;
import java.lang.*;
import java.io.*;
import java.math.*;
class Main{
static class Pair<T1, T2> {
public T1 first;
public T2 second;
public Pair(T1 first, T2 second) {
this.first = first;
this.second = second;
}
}
static final long INF = 1000000000000000000L;
static Map<Long, Pair<Integer,Integer>> ans;
static void init() {
ans = new HashMap<>();
for (long L = 1; L <= 2000000; L++) {
long n = L * (L + 1);
for (long R = L + 2; ; R++) {
long g = gcd(n, R);
if (n / g > INF / R) break;
n = n / g * R;
if (!ans.containsKey(n))
ans.put(n, new Pair<Integer,Integer>((int)L, (int)R));
}
}
}
static long gcd(long a, long b) { return b == 0 ? a : gcd(b, a % b);}
public static void main (String[] args) throws java.lang.Exception {
init();
Scanner sc = new Scanner(System.in);
int T = sc.nextInt();
while (T-- > 0) {
long n = sc.nextLong();
long sqrt_n = (long)Math.sqrt(n + 0.5);
Pair<Integer,Integer> res;
if (sqrt_n * (sqrt_n + 1) == n) {
res = new Pair<Integer,Integer>((int)sqrt_n, (int)(sqrt_n + 1));
if (ans.containsKey(n)) {
if (res.first > ans.get(n).first) res = ans.get(n);
}
} else {
if (ans.containsKey(n)) res = ans.get(n);
else {
System.out.println("-1");
continue;
}
}
System.out.println(res.first + " " + res.second);
}
}
}
用字典存储计算结果,n对应的答案是第一次存到字典中的[L, R]。
import math
INF = 10**18
ans = {}
def init():
global ans
for L in range(1, 2000000+1):
n = L * (L + 1)
R = L + 2
while True:
g = math.gcd(n, R)
if n // g > INF // R: break
n = n // g * R
if n not in ans: ans[n] = (L, R)
R += 1
init()
T = int(input())
for _ in range(T):
n = int(input())
sqrt_n = int(math.sqrt(n + 0.5))
if sqrt_n * (sqrt_n + 1) == n:
res = (sqrt_n, sqrt_n + 1)
if n in ans:
if res[0] > ans[n][0]:
res = ans[n]
else:
if n in ans: res = ans[n]
else:
print("-1")
continue
print(res[0], res[1])