友谊赛 - Friendly Match - 累加和&二分查找
友谊赛
(测试用例总数:X,1.5 秒 (C/C++),2 秒 (JAVA)/内存要求:256 M,堆栈 1 M)
A 国与 B 国每年都会举办一场友谊赛。该友谊赛在两国之间已举办多年,比赛规则如下:
比赛中,两国各派出 N 名参赛者,各国参赛者站成一排,面对面站立。然后,两国各派出站位相连的 K 名参赛者组成代表选手,作为一组进行比拼。比拼后,两国各自的 K 名选手返回初始位置,重新派出站位相连的 K 名选手组成队伍,进行多次比赛。注意,相同选手的组合仅能比拼一次。换句话说,如果 A 国的 1、2、3 号位选手与 B 国的 1、2、3 号位选手组队进行过一次比拼,则 A 国的 1、2、3 号位选手与 B 国的 1、2、3 号位选手都不得再次组队比拼。两国以这种方式进行比拼,直到无法再进行下去。
但是,由于是友谊赛,我们希望两国派出的站位相连的 K 名选手的能力值相加后的差值绝对值小于 X。
假设友谊赛按照上述条件进行,请算出两国之间比拼的总次数。
假设在五名选手中派出站位相连的三名参加比拼(如下所示),要求比拼中两国三名队员能力值之和的差值不大于二。
A 国选手能力值: 1 1 1 4 2
B 国选手能力值: 2 1 1 5 3
如果 A 国派出 1、2、3 号位选手组成一队(以下简称为 A123 队,A 国的能力值为 3),B 国只能派出其 1、2、3 号位选手组成队伍(以下简称为 B123 队,B 国的能力值为 4)与之比拼。除了可以和 A123 队(能力值之和为 3)比拼之外,B 国的 B123 队(能力值之和为 4)还可以与 A 国的 A234 队(能力值之和为 6)进行比拼,但是不能与 A345 队(能力值之和为 7)比拼。按此规则计算总比拼次数,则次数等于五。(A123 对 B123、A234 对 B123、A234 对 B234、A345 对 B234 以及 A345 对 B345)
[限制条件]
1. 各国参赛者数量 N 为介于 3 到 100,000 之间的整数。
2. 参与比拼的代表选手数量 K 为介于 2 到 N 之间的整数。
3. 各选手的能力值为介于 1 到 100,000 之间的整数。
4. 友谊赛的能力值差值 X 为介于 0 到 1,000,000,000 之间的整数。
[输入]
首先,给定测试用例数量 T,后面接着输入 T 种测试用例。在各个测试用例的第一行,给定两国参赛者数量 N、参与比拼的选手数量 K,以及比拼要求的能力值差值 X,以空格分隔。在第二行,按站位给定从 1 到 N 号位的 A 国 N 名选手的能力值,以空格分隔。在第三行,按站位给定从 1 到 N 号位的 B 国 N 名选手的能力值,以空格分隔。
[输出]
每个测试用例输出一行。首先,输出 “#x”(x 为测试用例编号,从 1 开始),加一个空格,然后输出案例规定条件下可进行的比拼总次数。
[输入和输出示例]
(输入)
3
5 3 2
1 1 1 4 2
2 1 1 5 3
10 3 3
2 2 2 2 2 3 3 3 3 3
1 1 1 1 1 2 2 2 1 1
5 3 100
10000 10 1000 100 1
1 2 3 4 5
(输出)
#1 5
#2 35
#3 0
思路:
1.分别求出A、B两组所有连续K个数的累加和;
2.将B组排序,遍历A组,从B中找A组元素-X至与找A组元素+X之间的元素的个数
3.由于数量级为10W,因此只能使用时间复杂度为O(NlogN)的算法
4.B中如果有相同的元素,A组元素-X对应的index应该取最小的,A组元素+X对应的index应该取最大的
5.由于B已排序,因此二分查找最快,且由于4的限制,Arrays.binarySearch()不满足,需自定义二分查找的方法
package pro; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.Arrays; import java.util.StringTokenizer; /** * 思路: * 1.分别求出A、B两组所有连续K个数的累加和; * 2.将B组排序,遍历A组,从B中找A组元素-X至与找A组元素+X之间的元素的个数 * 3.由于数量级为10W,因此只能使用时间复杂度为O(NlogN)的算法 * 4.B中如果有相同的元素,A组元素-X对应的index应该取最小的,A组元素+X对应的index应该取最大的 * 5.由于B已排序,因此二分查找最快,且由于4的限制,Arrays.binarySearch()不满足,需自定义二分查找的方法 * * @author XA-GDD * */ public class FriendlyMatch_0803 { static int T,N,K; static long X; static int _max_Nval = 100000; static int arrA [] = new int [_max_Nval+2]; static int arrB [] = new int [_max_Nval+2]; static long sumA [] = new long [_max_Nval]; static long sumB [] = new long [_max_Nval]; static int sumArrLength; static long ANS; public static void main(String[] args) throws IOException { System.setIn(new FileInputStream("D:\\workspace\\sw_pro\\test_case\\sample_input_0803.txt")); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); T = Integer.parseInt(st.nextToken()); for(int testCase=1;testCase<=T;testCase++) { Arrays.fill(arrA, 0); Arrays.fill(arrB, 0); Arrays.fill(sumA, 0L); Arrays.fill(sumB, 0L); ANS=0L; sumArrLength=0; st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); K = Integer.parseInt(st.nextToken()); X = Long.parseLong(st.nextToken()); st = new StringTokenizer(br.readLine()); for(int i=0;i<N;i++) { arrA[i] = Integer.parseInt(st.nextToken()); } st = new StringTokenizer(br.readLine()); for(int i=0;i<N;i++) { arrB[i] = Integer.parseInt(st.nextToken()); } //分别求K个数的累加和,时间复杂度O(N) sumA = getSumOfK(arrA); sumB = getSumOfK(arrB); Arrays.sort(sumB,0,sumArrLength); //遍历A数组,时间复杂度O(N) for(int i=0;i<sumArrLength;i++) { //二分查找,时间复杂度O(logN) int low = getLowBound(sumB,sumA[i]-X); int high = getHighBound(sumB,sumA[i]+X); int matchCnt = high-low; ANS += (long)matchCnt; } System.out.println("#"+testCase+" "+ANS); } } static long[] getSumOfK(int arr[]) { long [] sumArr = new long[_max_Nval]; long sum = 0L; for(int i=0;i<K;i++) { sum += (long)arr[i]; } sumArr[0] = sum; int index=0; for(int i=K;i<N;i++) { sum -= (long)arr[index++]; sum += (long)arr[i]; sumArr[index] = sum; } sumArrLength = index+1; return sumArr; } //低位,返回index最小位置 static int getLowBound(long arr[], long target) { int low=0; int high=sumArrLength; while (low < high) { int mid = (low + high) /2; long mdiVal = arr[mid]; if(target<=mdiVal) { high = mid; }else { low = mid+1; } } return low; } //高位,返回index最大+1位置 static int getHighBound(long arr[], long target) { int low=0; int high=sumArrLength; while (low < high) { int mid = (low + high) /2; long mdiVal = arr[mid]; if(target>=mdiVal) { low = mid+1; }else { high = mid; } } return low; } }