자료구조 공부/Segment Tree

백준 5676 (음주 코딩)

kdhoooon 2021. 3. 30. 17:46

문제


오늘은 ACM-ICPC 대회 전 날이다. 상근이는 긴장을 풀기 위해서 팀원들과 근처 술집으로 갔다.

상근이와 친구들은 다음 날 있을 대회를 연습하기 위해서 작은 게임을 하기로 했다.

먼저, 선영이는 상근이에게 총 N개의 정수로 이루어진 수열 X1, X2, ... XN을 적어준다. 게임은 총 K번 라운드로 이루어져있고, 매 라운드마다 선영이는 상근이에게 명령을 하나씩 내린다. 명령은 아래와 같이 두 가지가 있다.

  • 변경: 이 명령은 친구가 수열의 한 값을 바꾸고 싶을 때 사용한다.
  • 곱셈: 선영이는 상근이에게 i와 j를 말한다. 상근이는 Xi × Xi+1 × ... × Xj-1 × Xj 의 값이 양수인지, 음수인지, 0인지를 대답한다.

곱셈 명령에서 상근이가 대답을 잘못했을 때의 벌칙은 소주 한 잔이다. 상근이는 갑자기 대회가 걱정되기 시작했다. 또, 상근이는 발머의 피크 이론을 증명하고 싶지 않다.

다행히 선영이는 상근이에게 노트북 사용을 허락했다. 상근이는 자신의 수학 실력보다 코딩 실력을 더 믿는다.

상근이를 돕는 프로그램을 작성하시오.

 

 

 

 

풀이


Segment Tree 의 구간곱을 사용하면 쉽게 풀수 있다.

하지만 이문제의 핵심은 음수, 양수, 0 만 판단해 주면 된다.

 

따라서 저장을 -1, 1, 0 으로 하여 곱을 했을 때 부호를 구했다.

 

트리를 저장하는 코드를 보면

public static int init(int start, int end, int node) {
    	
   	if(start == end) {
   		if(list[start] > 0 ) {
   	   		return tree[node] = 1;
  		}
   		else if(list[start] == 0) {
   	   		return tree[node] = 0;
   		}
   		else {
  	   		return tree[node] = -1;   			
   		}

   	}
   	
   	int mid = (start + end) / 2;
   	    	
   	return tree[node] = init(start, mid, 2* node) * init(mid + 1, end, 2 * node + 1);
}

 

이와 같이 tree[node] 에 저장할 때 list[] 의 값이 음수, 양수, 0 이냐에 따라 값을 -1, 1, 0 으로 넣어주면 값이 overflow 가 발생하는 오류를 줄일 수 있다.

 

나머지 부분은 부분합, 부분곱 문제와 동일 하기에 따로 설명하지 않겠다.

 

conpulake.tistory.com/53

 

백준 11505 ( 구간 곱 구하기 )

문제 어떤 N개의 수가 주어져 있다. 그런데 중간에 수의 변경이 빈번히 일어나고 그 중간에 어떤 부분의 곱을 구하려 한다. 만약에 1, 2, 3, 4, 5 라는 수가 있고, 3번째 수를 6으로 바꾸고 2번째부터

conpulake.tistory.com

conpulake.tistory.com/50

 

백준 1275 ( 커피숍 2)

문제 모두 알다시피 동호는 커피숍의 마담이다. (마담이 무엇인지는 본인에게 물어보도록 하자.) 어느 날 커피숍의 손님 A씨가 동호에게 게임을 하자고 했다. 그 게임은 다음과 같은 규칙을 갖

conpulake.tistory.com

위의 문제들을 통해 Segment Tree 를 보면 더 좋을 것 같다.

 

이문제에서는 종료 조건이 EOF(End Of File) 이므로 이점을 유의해야한다.

<전체코드>

import java.io.*;
import java.util.*;


public class Main {
	
	static StringBuilder sb = new StringBuilder();
	static int[] tree;
	static int[] list;
	
    public static void main(String[] args) throws IOException {
       BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        
       String input;
       while( (input = bufferedReader.readLine()) != null) {
        	StringTokenizer st = new StringTokenizer(input);
        	
        	int n = Integer.parseInt(st.nextToken());
        	int k = Integer.parseInt(st.nextToken());
        	
        	tree = new int[4 * (n + 1)];
        	list = new int[n +1];
        	
        	
        	st = new StringTokenizer(bufferedReader.readLine());
        	
        	for(int j = 1 ; j <= n ; j++) {
        		list[j] = Integer.parseInt(st.nextToken()); 
        	}
        	
        	init(1, n, 1);
        	
        	for(int j = 0 ; j < k ; j++) {
        		st = new StringTokenizer(bufferedReader.readLine());
        		
        		String doing = st.nextToken();
        		int a = Integer.parseInt(st.nextToken());
        		int b = Integer.parseInt(st.nextToken());
        		
        		if(doing.equals("C")) {
        			update(1, n, 1, a, b);
        		}
        		else if(doing.equals("P")) {
        			int result = query(1, n, 1, a, b);
        			if(result > 0) {
        				sb.append("+");
        			}
        			else if(result == 0) {
        				sb.append("0");
        			}
        			else {
        				sb.append("-");
        			}
        		}
        	}
        	sb.append("\n");
        }
        
        System.out.println(sb);
    }
    
    public static int init(int start, int end, int node) {
    	
    	if(start == end) {
    		if(list[start] > 0 ) {
    	   		return tree[node] = 1;
    		}
    		else if(list[start] == 0) {
    	   		return tree[node] = 0;
    		}
    		else {
    	   		return tree[node] = -1;   			
    		}
 
    	}
    	
    	int mid = (start + end) / 2;
    	    	
    	return tree[node] = init(start, mid, 2* node) * init(mid + 1, end, 2 * node + 1);
    }
    
    
    public static int update(int start, int end, int node, int n, int num) {
    	
    	if(start == end) {
    		if(num > 0) {
    			return tree[node] = 1;
    		}
    		else if(num == 0) {
    			return tree[node] = 0;
    		}
    		else {
    			return tree[node] = -1;
    		}
    	}
    	
    	int mid = (start + end) / 2;
    	
    	if(mid >= n) {
    		return tree[node] =  tree[2 * node + 1] * update(start, mid, 2*node, n, num);
    	}
    	else {
    		return tree[node] = tree[2 * node] * update(mid + 1, end, 2 * node + 1, n, num);
    	}
    }
    
    public static int query(int start, int end, int node, int a, int b) {
    	
    	if(start > b || end < a) {
    		return 1;
    	}
    	else if(start >= a && end <= b){
    		return tree[node];
    	}
    	else {
    		int mid = (start + end) / 2;
    		
    		return query(start, mid, 2 * node, a, b) * query(mid + 1, end, 2 * node + 1, a, b);
    	}
    }
}

'자료구조 공부 > Segment Tree' 카테고리의 다른 글

백준 11505 ( 구간 곱 구하기 )  (0) 2021.03.05
백준 2357 (최솟값과 최댓값)  (0) 2021.03.05
백준 1168 (요세푸스 2)  (0) 2021.02.21
백준 1275 ( 커피숍 2)  (0) 2021.02.16