알고리즘/문제풀이

[인덱스트리] 구간 합 구하기

lipnus 2019. 3. 6. 11:37
반응형

백준 2042 구간 합 구하기

https://www.acmicpc.net/problem/2042


package 구간합구하기;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

static int N; //숫자개수
static int M; //변경횟수
static int K; //구간합을 구하는 횟수

static long[] tree;
static int k;
static int treeSize; //크기는 2^k
static int startIdx; //리프노드의 시작인덱스


public static void main(String[] args) throws Exception{

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());

N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
K = Integer.parseInt(st.nextToken());

//map개수 구하기
k=0;
while(N > Math.pow(2, k++)){}
treeSize = (int)Math.pow(2,k);
tree = new long[ treeSize ];
startIdx = treeSize/2;

//숫자 입력받음
for(int i=0; i<N; i++){
int a = Integer.parseInt(br.readLine());
tree[startIdx+i] = a;
}

//트리 채우기
fillTree(1);

//변경 및 부분합 구하기
for(int i=0; i<M+K; i++){
st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
int c = Integer.parseInt(st.nextToken());

//업데이트
if(a==1){
update(b,c); //b번째를 c로 바꾼다.
// print();
}

//합 구하기

if(a==2) System.out.println( sum(1,b,c, 1, (int)Math.pow(2,k-1)) );
}
}

static void print(){
for(long t: tree){
System.out.printf("%d ", t);
}
}


/**
*
* @param idx 현제위치
* @param left 구하려고 하는 구간의 시작
* @param right 구하려고 하는 구간의 끝
* @param start 현재위치노드 자손 리프노드의 시작
* @param end 현재위치노드 자손 리프노드의 끝
* @return
*/
static long sum(int idx, int left, int right, int start, int end){

//현재구간이 구하려는 구간 바깥
if(end < left || right < start){
return 0;
}

//현재 구간이 구하려는 구간의 안쪽
if(left <= start && end <=right){
return tree[idx];
}

//현재구간과 구하려는 구간이 겹침
return sum(idx*2, left, right, start, (start+end)/2) +
sum(idx*2+1, left, right, (start+end)/2+1, end);
}

/**
*
* @param idx 번째 수를
* @param num 으로 교체
*/
static void update(int idx, int num){

long delta = num - tree[startIdx + idx - 1];
int updateIdx=startIdx + idx - 1;

// System.out.printf("update(%d, %d) delta:%d, updateIndex:%d", idx, num, delta, updateIdx);


while(0<updateIdx){
tree[updateIdx] += delta;
updateIdx /= 2;
}
}


static long fillTree(int index){
if(index < treeSize/2) return tree[index] = fillTree(index*2) + fillTree(index*2+1);
else return tree[index];
}
}




예전에 풀었을때와 아주 약간 다른 차이점

public static void updateHeap(int index, long diff) {
arr[index] += diff;
if(index != 1) updateHeap(index/2, diff);
}

이번에는 update를 반복문으로 했는데 예전엔 재귀로 했었음.



//트리 나머지 부분 채우기
for(int i=arr.length-1; 1<i; i--) {
arr[i/2]+=arr[i];
}

이번에는 리프노드 채우는 걸 재귀로 했는데 예전엔 반복문으로 했었음.






예전에 푼 코드

public class Main {

static long[] arr;

public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub

//N개의 정수를 배열로 받음
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int N = Integer.parseInt( st.nextToken() );
int M = Integer.parseInt( st.nextToken() );
int K = Integer.parseInt( st.nextToken() );


int leafSize = (int) Math.pow(2.0, Math.floor((Math.log(N) / Math.log(2.0)) + 1));
int treeSize = 2 * leafSize;
// System.out.println("노드수: " + (treeSize-1));

arr = new long[ treeSize ];


//리프노드에 숫자 채우기
int startIndex = treeSize/2;
for(int i=startIndex; i<startIndex+N; i++) {
arr[i] = Integer.parseInt(br.readLine());
}

//트리 나머지 부분 채우기
for(int i=arr.length-1; 1<i; i--) {
arr[i/2]+=arr[i];
}

// 숫자뱐경 & 부분합구하기
for(int i=0; i<K+M; i++) {
st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
int c = Integer.parseInt(st.nextToken());

if(a==1) { //숫자변경
int index = startIndex+b-1;
long diff = c - arr[index];
updateHeap(index, diff);
}
else if(a==2){ //부분합
int left = startIndex+b-1;
int right = startIndex+c-1;
// System.out.println( countSum(left,right) );
System.out.println( sum(b,c,1,1,startIndex-1) );
}
}
}


public static long sum(long L,long R ,int idx,int temp_L,int temp_R){
if(temp_R<L||temp_L>R)return 0;
if(L <=temp_L&&temp_R<=R)return arr[idx];
int mid = (temp_R+temp_L)/2;
return sum(L,R,idx*2,temp_L,mid)+sum(L,R,idx*2+1,mid+1,temp_R);
}



public static long countSum(int left, int right) {
long sum=0;

while(left<right) {

//완료체크
if(left==right) {
sum += arr[left];
break;
}

//left
if(left%2==0) {
left = left/2;
}else {
sum += arr[left];
left++;
}

//완료체크
if(left==right){
sum += arr[left];
break;
}

//right
if(right%2==0) {
sum += arr[right];
right=(right-1)/2;
}else {
right=right/2;
}

}//while

return sum;
}


public static void updateHeap(int index, long diff) {
arr[index] += diff;
if(index != 1) updateHeap(index/2, diff);
}


public static void print(int[] arr) {
for(int i=1; i<arr.length; i++) {
System.out.printf("%d ", arr[i]);
}
}


}

countSum은 삼성SDS 강사분이 가르쳐준 방법.

sum처럼 하는게 더 깔끔한 것 같다.

반응형

'알고리즘 > 문제풀이' 카테고리의 다른 글

[힙] 더 맵게  (0) 2019.03.11
[투포인터] 부분합  (0) 2019.03.08
[Union&Find] 네트워크연결  (0) 2019.02.27
[그래프] LCA1  (0) 2019.02.26
[Heap] 절댓값 힙  (0) 2019.01.27