我正在做一个竞争性的编程问题,给你一组数字,然后是一定数量的查询。对于每个查询,您将获得2个整数,'a'和'b'。所以你应该输出数组中剩余元素的GCD(不包括a,b和它们之间的所有元素)。
例如,如果数组是:16,8,24,15,20,并且有2个查询(2,3)和(1,3),则输出1为:1,输出2为:5。 请注意,索引是基于1的。
这是我的代码,其中我用一个函数实现了基本思想,找到传递给它的数组的GCD。
public static void main(String args[]) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int t = Integer.parseInt(br.readLine());
while (t-- > 0) { //This is the number of test cases
String[] s1 = br.readLine().split(" ");
int n = Integer.parseInt(s1[0]); //Number of elements in array
int q = Integer.parseInt(s1[1]); //Number of queries
String[] s2 = br.readLine().split(" ");
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = Integer.parseInt(s2[i]);
}
for (int i = 0; i < q; i++) { //for each query
String[] s3 = br.readLine().split(" ");
int a = Integer.parseInt(s3[0]) - 1;
int b = Integer.parseInt(s3[1]) - 1;
int[] copy = new int[n - b + a - 1]; //this is so that the original array doesn't get messed up
int index = 0;
for (int j = 0; j < n; j++) { //filing the array without the elements of the query
if (j < a || j > b) {
copy[index] = arr[j];
index++;
}
}
int fin = gcd(copy);
System.out.println(fin);
}
}
}
private static int gcd(int a, int b) {
while (b > 0) {
int temp = b;
b = a % b; // % is remainder
a = temp;
}
return a;
}
private static int gcd(int[] input) { //simple GCD calculator using the fact that GCD(a,b,c) === GCD((a,b),c)
int result = input[0];
for (int i = 1; i < input.length; i++)
result = gcd(result, input[i]);
return result;
}
问题是我在某些部分上获得了AC(10个中的6个),其余部分则是TLE。有人可以建议一个更好的方法来解决这个问题,因为我的方法看起来太慢了,几乎不可能再进一步优化了吗?
答案 0 :(得分:2)
您可以为所有前缀和后缀预先计算gcd。每个查询都是前缀和后缀的联合,因此需要O(log MAX_A)
时间来回答一个。这是我的代码:
import java.util.*;
import java.io.*;
public class Solution {
static int gcd(int a, int b) {
while (b != 0) {
int t = a;
a = b;
b = t % b;
}
return a;
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(System.out);
int tests = Integer.parseInt(br.readLine());
for (int test = 0; test < tests; test++) {
String line = br.readLine();
String[] parts = line.split(" ");
int n = Integer.parseInt(parts[0]);
int q = Integer.parseInt(parts[1]);
int[] a = new int[n];
parts = br.readLine().split(" ");
for (int i = 0; i < n; i++)
a[i] = Integer.parseInt(parts[i]);
int[] gcdPrefix = new int[n];
int[] gcdSuffix = new int[n];
for (int i = 0; i < n; i++) {
gcdPrefix[i] = a[i];
if (i > 0)
gcdPrefix[i] = gcd(gcdPrefix[i], gcdPrefix[i - 1]);
}
for (int i = n - 1; i >= 0; i--) {
gcdSuffix[i] = a[i];
if (i < n - 1)
gcdSuffix[i] = gcd(gcdSuffix[i], gcdSuffix[i + 1]);
}
for (int i = 0; i < q; i++) {
parts = br.readLine().split(" ");
int left = Integer.parseInt(parts[0]);
int right = Integer.parseInt(parts[1]);
left--;
right--;
int res = 0;
if (left > 0)
res = gcd(res, gcdPrefix[left - 1]);
if (right < n - 1)
res = gcd(res, gcdSuffix[right + 1]);
out.println(res);
}
}
out.flush();
}
}
答案 1 :(得分:0)
“几乎不可能进一步优化”?算了吧:
input[i]
和input[j]
的GCD。请注意,这不会超过原始输入的一半大小。这可以扩展到更大的群体,但需要更多的空间。
答案 2 :(得分:0)
这里至关重要的是,一组数字A
的GCD等于A
的任何分区的GCD的GCD。例如,
GCD(16, 8, 24, 15, 20) = GCD(GCD(16, 8), GCD(24, 15, 20))
我会通过构建一些类似树的结构来利用这个事实。让我们为GCD[i, j]
和i
之间的索引元素集合的GCD写j
。对于大小为n
的给定输入,我会存储:
GCD[1, n]
GCD[1, n/2], GCD[n/2+1, n]
...
GCD[1, 2], GCD[2, 3] ... GCD[n-1, n]
也就是说,在树的每个级别,GCD的数量加倍,并且计算它们的集合的大小减半。请注意,您将以这种方式存储n-1
个数字,因此您需要线性额外存储空间。从头到尾计算它们,您需要将n-1
GCD操作作为预处理。
对于查询,您需要组合GCD,以便省略两个查询索引。例如,让我们有A
数组n = 8
,我们会查询(2, 4)
。
GCD[1, 8]
,因为我们需要排除2和4,所以我们在树中更深一层。GCD[1, 4]
,但我们可以使用GCD[5, 8]
,因为要排除的索引都不在其中。上半年我们需要更深入。GCD[1, 2]
,也无法使用GCD[3, 4]
,因此我们会更深入一层。A[1]
和A[3]
。我们现在需要计算GCD[5, 8]
,A[1]
和A[3]
的GCD。对于查询,我们只需要进行2次GCD计算,而不是以天真的方式进行5次计算。
一般情况下,您将花费O(log n)
时间搜索结构,每次查询需要O(log n)
次GCD计算。