{ }codelper
쌓아 가기while 반복큰 정수(64비트)P0296

오를수록 멀어지는 다음 레벨

게임 캐릭터가 레벨 1, 경험치 0에서 시작한다. 다음 레벨로 오르는 데 필요한 경험치를 문턱이라 한다.
처음 문턱은 a이고, 처음 증가폭은 d이다. 사건 q개가 차례로 일어나고, 사건마다 0이 아닌 정수 x가 하나 주어진다. x가 양수이면 경험치를 얻는다.
이번 사건이 끊기지 않고 이어진 양수 사건 가운데 k번째이면 경험치에 x + (k − 1) × b를 더한다.
첫 사건이거나 바로 앞 사건이 벌점이었으면 k = 1이고, 바로 앞 사건이 양수였으면 k는 그 사건의 k보다 1 크다. x가 음수이면 벌점이다.
경험치에서 −x를 빼고, 그 결과가 0보다 작으면 경험치를 0으로 한다.
벌점은 연속을 끊어서, 그다음 양수 사건은 다시 1번째가 된다. 사건마다 경험치를 고친 다음, 레벨이 M보다 작고 경험치가 문턱 이상인 동안 다음을 되풀이한다: 경험치에서 문턱을 빼고, 레벨을 1 올리고, 문턱에 증가폭을 더하고, 그다음 증가폭에 g를 더한다. 그래서 사건 하나로 여러 레벨을 오를 수 있고, 남은 경험치는 다음으로 넘어간다. 레벨은 내려가지 않는다.
레벨이 M이 되면 더 오르지 않고, 경험치는 빼지 않은 채 계속 쌓인다. 첫 줄에 a, d, g, M, b가 빈칸 하나씩으로 띄워 주어지고, 둘째 줄에 q가, 다음 q줄에 x가 하나씩 주어진다. 사건마다 그 사건을 처리한 뒤의 레벨과 경험치를 빈칸 하나로 띄워 한 줄에 출력하라. 마지막 줄에는 사건 하나에서 오른 레벨 수 가운데 가장 큰 값을 출력하라(한 번도 오르지 않았으면 0).

입력
1 ≤ a ≤ 1,000,000,000, 0 ≤ d ≤ 1,000,000, 0 ≤ g ≤ 1,000, 1 ≤ M ≤ 10,000, 0 ≤ b ≤ 1,000,000, 1 ≤ q ≤ 1,000, 1 ≤ |x| ≤ 1,000,000,000.
모두 정수이고, 쉼표 없이 숫자만 주어진다
출력
q + 1줄.
사건마다 레벨과 경험치, 마지막 줄에 한 사건에서 가장 많이 오른 레벨 수.
수는 쉼표 없이 숫자만 쓴다
예시 1 · stdin
10 5 2 5 3
6
12
60
-25
50
7
100
stdout
2 2
4 28
4 3
5 22
5 32
5 138
2
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

반복이 돌 때마다 앞 바퀴의 값에 이어서 쌓아 가는 변수가 다섯 개예요(경험치, 문턱, 증가폭, 연속 횟수, 가장 많이 오른 수).
xp += x 같은 +=는 여덟 언어에 모두 있고, level++는 Python에만 없어요(level += 1).
함정 ① 사건 하나로 여러 레벨을 오를 수 있어서 if가 아니라 while(크레이콘 동안)로 '문턱 이상인 동안' 되풀이해요.
if로 쓰면 예시의 둘째 사건에서 레벨 3에 멈춰요.
② 문턱에 증가폭을 더한 다음 증가폭에도 g를 더해야 해요.
쌓는 값이 또 다른 쌓는 값에 쌓이는 꼴이라 한 줄을 빼먹기 쉬워요.
③ >=를 >로 쓰면 경험치가 문턱과 딱 같을 때 오르지 못해요.
④ 최고 레벨에서는 level < M으로 멈춰야 해요.
이 조건이 없으면 M을 넘어서도 올라요.
⑤ 벌점은 0 아래로 내려가지 않게 잘라요: Python max(0, v), JS·TS·Java Math.max(0, v), C# Math.Max(0, v), C++ std::max(0LL, v)(두 인수의 타입이 같아야 해서 0이 아니라 0LL), 크레이콘 수학.큰수(0, v).
C 표준 라이브러리에는 정수용 max 함수가 없어서 if (xp < 0) xp = 0;으로 써요.
⑥ 벌점이 나오면 연속 횟수를 0으로 되돌려야 하고, k번째 연속의 보너스는 k × b가 아니라 (k − 1) × b예요.
⑦ 경험치는 약 1.5조까지 커지고 문턱도 21억을 넘을 수 있어서 int(약 21억)에 담기지 않아요.
C·C++은 long long(printf는 %lld), Java·C#은 long을 써요.
Python 정수는 크기에 끝이 없고, JS·TS·크레이콘의 수는 2^53(약 9 × 10^15)까지 정수가 정확해서 그대로 돼요.

Pythonmain.py · 31 줄
import sys lines = sys.stdin.read().split("\n")a, d, g, M, b = map(int, lines[0].split())q = int(lines[1]) level, xp = 1, 0need, step = a, d  # 문턱과 증가폭: 오를 때마다 둘 다 쌓인다streak = 0  # 연달아 얻은 횟수best = 0  # 한 사건에서 가장 많이 오른 레벨 수out = []for i in range(q):    x = int(lines[2 + i])    if x > 0:        streak += 1        xp += x + (streak - 1) * b    else:        streak = 0        xp = max(0, xp + x)    ups = 0    # 한 번에 여러 레벨을 오를 수 있으니 if가 아니라 while    while level < M and xp >= need:        xp -= need        level += 1        need += step        step += g        ups += 1    best = max(best, ups)    out.append(f"{level} {xp}")out.append(str(best))print("\n".join(out))
JavaScriptmain.js · 32 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n");const [a, d, g, M, b] = lines[0].trim().split(/\s+/).map(Number);const q = Number(lines[1]); let level = 1, xp = 0;let need = a, step = d; // 문턱과 증가폭: 오를 때마다 둘 다 쌓인다let streak = 0; // 연달아 얻은 횟수let best = 0; // 한 사건에서 가장 많이 오른 레벨 수const out = [];for (let i = 0; i < q; i++) {  const x = Number(lines[2 + i]);  if (x > 0) {    streak += 1;    xp += x + (streak - 1) * b;  } else {    streak = 0;    xp = Math.max(0, xp + x);  }  let ups = 0;  // 한 번에 여러 레벨을 오를 수 있으니 if가 아니라 while  while (level < M && xp >= need) {    xp -= need;    level += 1;    need += step;    step += g;    ups += 1;  }  best = Math.max(best, ups);  out.push(`${level} ${xp}`);}out.push(String(best));console.log(out.join("\n"));
TypeScriptmain.ts · 34 줄
import { readFileSync } from "node:fs"; const lines: string[] = readFileSync(0, "utf8").split("\n");const [a, d, g, M, b]: number[] = lines[0].trim().split(/\s+/).map(Number);const q = Number(lines[1]); let level = 1, xp = 0;let need = a, step = d; // 문턱과 증가폭: 오를 때마다 둘 다 쌓인다let streak = 0; // 연달아 얻은 횟수let best = 0; // 한 사건에서 가장 많이 오른 레벨 수const out: string[] = [];for (let i = 0; i < q; i++) {  const x = Number(lines[2 + i]);  if (x > 0) {    streak += 1;    xp += x + (streak - 1) * b;  } else {    streak = 0;    xp = Math.max(0, xp + x);  }  let ups = 0;  // 한 번에 여러 레벨을 오를 수 있으니 if가 아니라 while  while (level < M && xp >= need) {    xp -= need;    level += 1;    need += step;    step += g;    ups += 1;  }  best = Math.max(best, ups);  out.push(`${level} ${xp}`);}out.push(String(best));console.log(out.join("\n"));
JavaMain.java · 41 줄
import java.util.Scanner; public class Main {    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        long a = sc.nextLong(), d = sc.nextLong(), g = sc.nextLong();        int M = sc.nextInt();        long b = sc.nextLong();        int q = sc.nextInt();         int level = 1;        long xp = 0; // 1조 넘게 쌓여서 int로는 모자란다        long need = a, step = d; // 문턱과 증가폭: 오를 때마다 둘 다 쌓인다        long streak = 0; // 연달아 얻은 횟수        int best = 0; // 한 사건에서 가장 많이 오른 레벨 수        StringBuilder out = new StringBuilder();        for (int i = 0; i < q; i++) {            long x = sc.nextLong();            if (x > 0) {                streak += 1;                xp += x + (streak - 1) * b;            } else {                streak = 0;                xp = Math.max(0, xp + x);            }            int ups = 0;            // 한 번에 여러 레벨을 오를 수 있으니 if가 아니라 while            while (level < M && xp >= need) {                xp -= need;                level += 1;                need += step;                step += g;                ups += 1;            }            best = Math.max(best, ups);            out.append(level).append(' ').append(xp).append('\n');        }        out.append(best).append('\n');        System.out.print(out);    }}
C#Main.cs · 49 줄
using System;using System.Text; class Program{    static void Main()    {        string[] p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        long a = long.Parse(p[0]), d = long.Parse(p[1]), g = long.Parse(p[2]);        int M = int.Parse(p[3]);        long b = long.Parse(p[4]);        int q = int.Parse(Console.ReadLine().Trim());         int level = 1;        long xp = 0; // 1조 넘게 쌓여서 int로는 모자란다        long need = a, step = d; // 문턱과 증가폭: 오를 때마다 둘 다 쌓인다        long streak = 0; // 연달아 얻은 횟수        int best = 0; // 한 사건에서 가장 많이 오른 레벨 수        var output = new StringBuilder();        for (int i = 0; i < q; i++)        {            long x = long.Parse(Console.ReadLine().Trim());            if (x > 0)            {                streak += 1;                xp += x + (streak - 1) * b;            }            else            {                streak = 0;                xp = Math.Max(0, xp + x);            }            int ups = 0;            // 한 번에 여러 레벨을 오를 수 있으니 if가 아니라 while            while (level < M && xp >= need)            {                xp -= need;                level += 1;                need += step;                step += g;                ups += 1;            }            best = Math.Max(best, ups);            output.Append(level).Append(' ').Append(xp).Append('\n');        }        output.Append(best).Append('\n');        Console.Write(output);    }}
C++main.cpp · 38 줄
#include <algorithm>#include <iostream> int main() {    std::ios::sync_with_stdio(false);    long long a, d, g, b;    int M, q;    std::cin >> a >> d >> g >> M >> b >> q;     int level = 1;    long long xp = 0;             // 1조 넘게 쌓여서 int로는 모자란다    long long need = a, step = d; // 문턱과 증가폭: 오를 때마다 둘 다 쌓인다    long long streak = 0;         // 연달아 얻은 횟수    int best = 0;                 // 한 사건에서 가장 많이 오른 레벨 수    for (int i = 0; i < q; i++) {        long long x;        std::cin >> x;        if (x > 0) {            streak += 1;            xp += x + (streak - 1) * b;        } else {            streak = 0;            xp = std::max(0LL, xp + x); // 두 인수의 타입이 같아야 해서 0LL        }        int ups = 0;        // 한 번에 여러 레벨을 오를 수 있으니 if가 아니라 while        while (level < M && xp >= need) {            xp -= need;            level += 1;            need += step;            step += g;            ups += 1;        }        best = std::max(best, ups);        std::cout << level << ' ' << xp << '\n';    }    std::cout << best << '\n';}
Cmain.c · 38 줄
#include <stdio.h> int main(void) {    long long a, d, g, b;    int M, q;    scanf("%lld %lld %lld %d %lld %d", &a, &d, &g, &M, &b, &q);     int level = 1;    long long xp = 0;             /* 1조 넘게 쌓여서 int로는 모자란다 */    long long need = a, step = d; /* 문턱과 증가폭: 오를 때마다 둘 다 쌓인다 */    long long streak = 0;         /* 연달아 얻은 횟수 */    int best = 0;                 /* 한 사건에서 가장 많이 오른 레벨 수 */    for (int i = 0; i < q; i++) {        long long x;        scanf("%lld", &x);        if (x > 0) {            streak += 1;            xp += x + (streak - 1) * b;        } else {            streak = 0;            xp += x;            if (xp < 0) xp = 0;        }        int ups = 0;        /* 한 번에 여러 레벨을 오를 수 있으니 if가 아니라 while */        while (level < M && xp >= need) {            xp -= need;            level += 1;            need += step;            step += g;            ups += 1;        }        if (ups > best) best = ups;        printf("%d %lld\n", level, xp);    }    printf("%d\n", best);    return 0;}
Craykonmain.cray · 41 줄
p = 콘솔.수들읽기();a = p[0];d = p[1];g = p[2];M = p[3];b = p[4];q = 콘솔.줄읽기().수로변환(); level = 1;xp = 0;need = a; // 문턱step = d; // 증가폭: 오를 때마다 문턱과 함께 쌓인다streak = 0; // 연달아 얻은 횟수best = 0; // 한 사건에서 가장 많이 오른 레벨 수반복 (i = 0; i < q; i++){    x = 콘솔.줄읽기().수로변환();    만약 (x > 0)    {        streak += 1;        xp += x + (streak - 1) * b;    }    아니면    {        streak = 0;        xp = 수학.큰수(0, xp + x);    }    ups = 0;    // 한 번에 여러 레벨을 오를 수 있으니 만약이 아니라 동안    동안 (level < M && xp >= need)    {        xp -= need;        level += 1;        need += step;        step += g;        ups += 1;    }    best = 수학.큰수(best, ups);    콘솔.쓰기(level + " " + xp);}콘솔.쓰기(best);