友谊赛 - 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;
}
}
浙公网安备 33010602011771号