피자와 피자 각각에 대해서 피자를 고를 수 있는 경우의 수를 미리 구해놓으면 전체 경우의 수를 쉽게 구할 수 있습니다.
피자에서 길이가 인 연속한 구간을 고르는 경우의 수
피자에서 길이가 인 연속한 구간을 고르는 경우의 수로 정의하면
는 최대 이므로 배열을 선언할 수 있습니다.
손님이 구매하고자 하는 피자크기 에 대해 를 구해서 모두 더해주면 됩니다. 이 때, 혹은 피자에서 아예 고르지 않는 경우의 수도 있음에 유의합니다.
임의의 길이의 구간에서 모든 연속한 구간을 고르는 경우의 수는 입니다. 원형이 아니였다면 인 를 모두 구하면 되겠지만 원형이라서 시작점에 걸치는 경우도 있다는 것을 고려해야합니다. 경우의 수가 중복되지 않도록 여기서는 길이 를 정하고 길이별로 구분해서 모든 경우의 수를 구합니다. 단 인 경우와 인 경우는 예외처리 해줍시다.
이렇게 구하다 보면 길이 가 길어서 배열의 바깥을 나가는 경우가 있습니다. 이럴 때는 배열 바깥에서 튀어나온 만큼 처음에서 다시 시작하게 하면 됩니다.
public class Main {
// 피자판의 모든 원소를 연속적으로 선택해서 만들수 있는
// 경우의 수를 나타내는 배열 반환
static int[] f(int N, int[] S) {
int[] ret = new int[1000001];
// 하나도 안 선택하는 것도 경우의 수 중 하나
ret[0]++;
for (int i = 1; i <= N; i++)
// 길이가 k인 구간을 선택
// 단 전체 구간은 중복 될 수 있으므로 예외
for (int k = 1; k <= N - 1; k++)
// 한 바퀴 도는 경우
if (i + k - 1 > N) {
ret[S[N] - S[i - 1] + S[i + k - 1 - N]]++;
} else {
ret[S[i + k - 1] - S[i - 1]]++;
}
ret[S[N]]++;
return ret;
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int K = Integer.parseInt(br.readLine());
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
int N = Integer.parseInt(st.nextToken());
int M = Integer.parseInt(st.nextToken());
int[] S1 = new int[N + 1]; int[] S2 = new int[M + 1];
for (int i = 1; i <= N; i++) S1[i] = Integer.parseInt(br.readLine()) + S1[i - 1];
for (int i = 1; i <= M; i++) S2[i] = Integer.parseInt(br.readLine()) + S2[i - 1];
int[] R1 = f(N, S1); int[] R2 = f(M, S2);
long ret = 0;
for (int i = 0; i <= K; i++)
ret += (long)R1[i] * R2[K - i];
System.out.println(ret);
}
}
피자 의 전처리에
길이가 인 피자를 만드는 경우를 모두 시도하는데
입니다.