import java.io.*;
import java.util.*;
class Main{
public static void main(String[] args) throws IOException {
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[][] map = new int[n+1][n+1];
int[][] prepix = new int[n+1][n+1];
for(int i=1; i<n+1; i++) {
int[] arr = Arrays.stream(br.readLine().split(" ")).mapToInt(Integer::parseInt).toArray();
for(int j=1; j<n+1; j++) {
map[i][j] = arr[j-1];
prepix[i][j] = prepix[i][j-1]+prepix[i-1][j]-prepix[i-1][j-1]+arr[j-1];
}
}
// m만큼 실행
for(int i=0; i<m; i++) {
st = new StringTokenizer(br.readLine());
int x1 = Integer.parseInt(st.nextToken());
int y1 = Integer.parseInt(st.nextToken());
int x2 = Integer.parseInt(st.nextToken());
int y2 = Integer.parseInt(st.nextToken());
int result = prepix[x2][y2]-prepix[x2][y1-1]-prepix[x1-1][y2]+prepix[x1-1][y1-1];
System.out.println(result);
}
}
}
#### [[백준 10986번]](https://www.acmicpc.net/problem/10986) 나머지합 구하기
import java.io.;
import java.util.;
class Main{
public static void main(String[] args) throws IOException {
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());
long cnt = 0;
long[] prefix = new long[n];
long[] arr = new long[m];
st = new StringTokenizer(br.readLine());
prefix[0] = Integer.parseInt(st.nextToken());
for(int i=1; i<n; i++) {
prefix[i] = prefix[i-1]+Integer.parseInt(st.nextToken());
}
for(int i=0; i<n; i++) {
int r = (int) (prefix[i]%m);
if(r==0) cnt++;
arr[r]++;
}
for(int i=0; i<m; i++) {
if(arr[i]>1) cnt+=arr[i]*(arr[i]-1)/2;
}
System.out.println(cnt);
}
}
### 투포인터
- 2개의 포인터를 이용해 시간복잡도를 최적화하는 알고리즘
- start, end index가 둘 다 0인 경우와, 처음과 끝인 경우를 구분하는 점이 어려웠던 알고리즘
#### [[백준 1940번]](https://www.acmicpc.net/problem/1940) 주몽의 명령
import java.io.;
import java.util.;
class Main{
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
int m = Integer.parseInt(br.readLine());
int[] arr = new int[n];
StringTokenizer st = new StringTokenizer(br.readLine());
for(int i=0; i<arr.length; i++) {
arr[i] = Integer.parseInt(st.nextToken());
}
Arrays.sort(arr);
int start = 0;
int end = n-1;
int cnt = 0;
while(start<end) {
int k = arr[start]+arr[end];
if(k == m) {
cnt++;
start++;
end--;
} else if(k<m) {
start++;
} else {
end--;
}
}
System.out.println(cnt);
}
}
### 슬라이딩윈도우
- 두개의 포인터로 범위를 지정한 다음, 그 범위를 유지한 채로 이동하며 문제를 해결하는 알고리즘
#### [[백준 12891번]] (https://www.acmicpc.net/problem/12891) DNA 비밀번호
import java.io.;
import java.util.;
class Main{
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int s = Integer.parseInt(st.nextToken());
int p = Integer.parseInt(st.nextToken());
String[] arr = br.readLine().split("");
st = new StringTokenizer(br.readLine());
int[] cnt = new int[4];
for(int i=0; i<cnt.length; i++) {
cnt[i] = Integer.parseInt(st.nextToken());
}
int result = 0;
int[] window = new int[4];
for(int i=0; i<p; i++) {
if(arr[i].equals("A")) window[0]++;
else if(arr[i].equals("C")) window[1]++;
else if(arr[i].equals("G")) window[2]++;
else if(arr[i].equals("T")) window[3]++;
}
for(int i=0; i<4; i++) {
if(window[i]<cnt[i]) break;
if(i==3) result++;
}
for(int i=1; i<s-p+1; i++) {
if(arr[i-1].equals("A")) window[0]--;
else if(arr[i-1].equals("C")) window[1]--;
else if(arr[i-1].equals("G")) window[2]--;
else if(arr[i-1].equals("T")) window[3]--;
if(arr[i+p-1].equals("A")) window[0]++;
else if(arr[i+p-1].equals("C")) window[1]++;
else if(arr[i+p-1].equals("G")) window[2]++;
else if(arr[i+p-1].equals("T")) window[3]++;
for(int j=0; j<4; j++) {
if(window[j]<cnt[j]) {
break;
}
if(j==3) {
result++;
}
}
}
System.out.println(result);
}
}