锻炼 - Exercise - Indexed Tree
锻炼
时间限制:最多40个用例,1.5秒(C/C++),2秒(Java)/内存限制:256 MB(Stack 1 MB)
哲珉常去的健身房进了一种新的健身器材。该器材是以两柱子竖立的形态,每个柱子上有N+1个把手,各把手都以从下到上的方向贴着0到N的编号。
使用该器材进行锻炼时,需要用左手握住左侧柱子的0号把手,用右手握住右侧柱子的0号把手。然后,将双手向上移动到更高的把手来持续进行锻炼。
该器材附有一张表,表上以成对的形式写着多个左右把手的编号。锻炼者只能同时握住表上写着的一对把手编号。即,若表上有(3, 4),那么可以同时握住左侧3号把手和右侧4号把手。若表中没有(2, 6),那么不能同时握住左侧2号把手和右侧6号把手。
哲珉希望通过一次获得最大程度的锻炼,但锻炼量和他同时移动双手的次数成正比。换句话说,当双手移动的次数越多,能达到的锻炼量就越大。

比如说,假设N=10,并且表中的成对编号为 (2, 3)、(1, 1)、(5, 5)、(7, 4)、(6, 8)、(10, 1)。在这种情况下,双手移动次数最多的一种方法是以 (0, 0)、(1, 1)、(2, 3)、(5, 5)、(6, 8) 的顺序握住把手。这时,双手移动的次数为四次,然而锻炼量达到了最大。
请写一个程序,通过接收健身器材的表来计算双手能移动的最大次数。
[限制条件]
1.把手的最大编号N是介于1到1,000,000,000之间。
2.给出的编号成对个数M是介于1到300,000之间。
[输入]
第一行给出测试用例数量T。接着给出T个测试用例。各个用例的第一行给出把手编号的最大值N和成对个数M。接下来通过M行,每行给出一对左右把手的编号。
[输出]
各测试用例的答案按照顺序标准输出。每个测试用例输出“#x”(其中x表示测试用例编号,从1开始),加一个空格(不带引号),输出题目中要求的双手能移动的最大次数。
[输入输出 示例]
(输入)
2
10 6
2 3
1 1
5 5
7 4
6 8
10 1
14 8
8 10
1 2
3 4
2 3
14 13
4 4
9 13
7 9
(输出)
#1 4
#2 6
思路:数据有两列,如果对一列排序,那么只需要求另一列的LIS即可
因此,可使用Indexed Tree或者DP+二分查找的方式
使用Indexed Tree时,因为N的范围是10亿,如果直接使用N开数组,会超过可声明的范围,因此需要散列化
package tree;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.TreeSet;
/**
* 思路:数据有两列,如果对一列排序,那么只需要求另一列的LIS即可
* 因此,可使用Indexed Tree或者DP+二分查找的方式
* 使用Indexed Tree时,因为N的范围是10亿,如果直接使用N开数组,会超过可声明的范围,因此需要散列化
* @author XA-GDD
*
*/
public class Exercise_0628 {
static int T,N,M;
static int _Max_M = 300000;
static int [][] srcArr = new int[_Max_M+1][2];
static int [] idxTree = new int[_Max_M*2];
static int offset;
static TreeSet<Integer> ts = new TreeSet<Integer>();
static HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
public static void main(String[] args) throws IOException {
System.setIn(new FileInputStream("D:\\workspace\\sw_pro\\test_case\\sample_input_0628.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++) {
init();
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
for(int i=0;i<M;i++) {
st = new StringTokenizer(br.readLine());
srcArr[i][0]=Integer.parseInt(st.nextToken());
srcArr[i][1]=Integer.parseInt(st.nextToken());
ts.add(srcArr[i][1]);
}
//对左侧的数据排序,左侧相同时,右侧逆序
Arrays.sort(srcArr,0,M,new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
if(o1[0]==o2[0]) {
return o2[1]-o1[1];
}
return o1[0]-o2[0];
}
});
//散列化
int index=0;
for (int val : ts) {
map.put(val, index++);
}
//indexed tree初始化
int k=0;
while((1<<k)<index) {
k++;
}
offset=1<<k;
for(int i=0;i<M;i++) {
int val = query(offset,offset+map.get(srcArr[i][1])-1); //0~当前-1处LIS的最大值
update(offset+map.get(srcArr[i][1]),val+1); //当前处的LIS长度=当前-1处的LIS+1
}
System.out.println("#"+testCase+" "+idxTree[1]);
}
}
static void update(int index,int val) {
idxTree[index]=val;
index = index>>1;
while(index>0) {
idxTree[index] = Math.max(idxTree[index*2], idxTree[index*2+1]);
index = index>>1;
}
}
static int query(int start, int end) {
int res =0;
while(start<=end) {
if(start%2==1) {
res = Math.max(res, idxTree[start]);
}
if(end%2==0) {
res = Math.max(res, idxTree[end]);
}
start=(start+1)>>1;
end=(end-1)>>1;
}
return res;
}
static void init() {
for(int i=0;i<srcArr.length;i++) {
Arrays.fill(srcArr[i], 0);
}
Arrays.fill(idxTree, 0);
ts.clear();
map.clear();
}
}
浙公网安备 33010602011771号