{ }codelper
常量溢出整数除法P0387

流量套餐的超额费用

某通信公司的套餐每月包含 3 GB 500 MB 的流量。
超出套餐内流量的部分按每份 100 MB 来计数。
超出的量即使不满一份,也算作一份。
每一份要多付 550 韩元。
如果用量等于或少于套餐内的流量,那么超出的份数是 0,多付的费用也是 0 韩元。
单位换算是 1 KB = 1,024 B,1 MB = 1,024 KB,1 GB = 1,024 MB。
给出一个月内的 n 条使用记录,求用掉的流量总和、超出的份数和多付的费用。

输入
第一行给出使用记录的条数 n(1 ≤ n ≤ 300)。
接下来 n 行每行给出表示用量的整数 a 和单位 u,用一个空格隔开(1 ≤ a ≤ 1,023)。
u 是 B、KB、MB、GB 之一。
输出
第一行输出用掉的流量总和,是以 B 为单位的整数。
第二行输出超出的份数。
第三行输出多付的费用,是以韩元为单位的整数。
三个数都只输出数字,不带逗号。
样例 1 · stdin
4
3 GB
900 MB
512 KB
100 B
stdout
4165468260
5
2750
样例 2 · stdin
2
600 MB
3 GB
stdout
3850371072
1
550
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

给不变的值起名字的常量,各语言的写法并不相同。
Python 没有常量语法,只能像 KB = 1024 这样用大写名字写下来,并约定不去改它。
再次赋值也不会报错。
JavaScript 的 const 再次赋值时会在运行中抛出 TypeError,TypeScript 则提前用编译错误拦住。
Java 用 static final,C# 用 const,C++ 用 constexpr,三者再次赋值都是编译错误。
C 用 #define KB 1024LL 这样的宏,而宏替换的不是值,而是原样的文本。
Craykon 没有常量,所以用约定不去改的变量来代替。
像 MB = 1024 * KB 这样用一个常量造出另一个常量,1,024 这个数就只需写在一处。
陷阱一:常量表达式的溢出。
3 GB 是 3,221,225,472 B,放不进 32 位整数(最大 2,147,483,647)。
在 Java 中写 static final long ALLOWANCE = 3 * 1024 * 1024 * 1024 + 500 * 1024 * 1024; 时,在存入 long 之前先按 int 相乘,不报任何错误就变成 -549,453,824。
示例 1 的份数不是 5 而是 45。
对 3 * 1024 * 1024 * 1024,C# 的 const 和 C++ 的 constexpr 用编译错误拦住,C 只给出警告就通过编译(结果没有定义)。
把第一个值写成 64 位整数,比如 1024L,就可以了。
Python、JavaScript 和 Craykon 在这个大小的整数上不会溢出。
陷阱二:没有括号的宏。
在 C 中写 #define ALLOWANCE 3 * GB + 500 * MB 时,total - ALLOWANCE 会展开成 total - 3 * GB + 500 * MB,500 MB 不是被减去而是被加上。
示例 1 的份数变成 15。
陷阱三:单位。
本题的 1 KB 是 1,024 B,不是 1,000 B。
如果把那一行常量写成 1000,示例 1 的第一行就不是 4165468260 而是 3900512100。
陷阱四:向上取整。
示例 1 中超出的量是 419,954,788 B,比 4 份多一点,所以是 5 份。
向下取整会得到 4。
陷阱五:正好整除的时候。
示例 2 中超出的量正好是 100 MB,所以是 1 份。
把向上取整算成“商 + 1”会得到 2。

Pythonmain.py · 24 行
import sys # 파이썬에는 상수 문법이 없다: 대문자 이름은 "바꾸지 말자"는 약속일 뿐이다KB = 1024MB = 1024 * KBGB = 1024 * MBALLOWANCE = 3 * GB + 500 * MB  # 기본으로 주는 양BLOCK = 100 * MB               # 넘은 양을 세는 묶음PRICE = 550                    # 묶음 하나의 요금(원)UNIT = {"B": 1, "KB": KB, "MB": MB, "GB": GB} lines = sys.stdin.read().split("\n")n = int(lines[0])total = 0for i in range(1, n + 1):    a, u = lines[i].split()    total += int(a) * UNIT[u] blocks = 0if total > ALLOWANCE:    blocks = (total - ALLOWANCE + BLOCK - 1) // BLOCK  # 올림print(total)print(blocks)print(blocks * PRICE)
JavaScriptmain.js · 24 行
// const는 다시 넣으면 실행 중에 TypeError가 난다const KB = 1024;const MB = 1024 * KB;const GB = 1024 * MB;const ALLOWANCE = 3 * GB + 500 * MB; // 기본으로 주는 양const BLOCK = 100 * MB; // 넘은 양을 세는 묶음const PRICE = 550; // 묶음 하나의 요금(원)const UNIT = { B: 1, KB: KB, MB: MB, GB: GB }; const lines = require("fs").readFileSync(0, "utf8").split("\n");const n = Number(lines[0]);let total = 0;for (let i = 1; i <= n; i++) {  const [a, u] = lines[i].trim().split(" ");  total += Number(a) * UNIT[u];} let blocks = 0;if (total > ALLOWANCE) {  blocks = Math.floor((total - ALLOWANCE + BLOCK - 1) / BLOCK); // 올림}console.log(String(total));console.log(String(blocks));console.log(String(blocks * PRICE));
TypeScriptmain.ts · 26 行
import { readFileSync } from "node:fs"; // const는 다시 넣으면 컴파일 오류다const KB: number = 1024;const MB: number = 1024 * KB;const GB: number = 1024 * MB;const ALLOWANCE: number = 3 * GB + 500 * MB; // 기본으로 주는 양const BLOCK: number = 100 * MB; // 넘은 양을 세는 묶음const PRICE: number = 550; // 묶음 하나의 요금(원)const UNIT: Record<string, number> = { B: 1, KB: KB, MB: MB, GB: GB }; const lines: string[] = readFileSync(0, "utf8").split("\n");const n: number = Number(lines[0]);let total: number = 0;for (let i = 1; i <= n; i++) {  const [a, u] = lines[i].trim().split(" ");  total += Number(a) * UNIT[u];} let blocks: number = 0;if (total > ALLOWANCE) {  blocks = Math.floor((total - ALLOWANCE + BLOCK - 1) / BLOCK); // 올림}console.log(String(total));console.log(String(blocks));console.log(String(blocks * PRICE));
JavaMain.java · 40 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader; public class Main {    // static final 상수. 1024L처럼 L을 붙여야 곱셈이 long으로 계산된다    static final long KB = 1024L;    static final long MB = 1024 * KB;    static final long GB = 1024 * MB;    static final long ALLOWANCE = 3 * GB + 500 * MB; // 기본으로 주는 양    static final long BLOCK = 100 * MB;              // 넘은 양을 세는 묶음    static final long PRICE = 550;                   // 묶음 하나의 요금(원)     static long unit(String u) {        switch (u) {            case "KB": return KB;            case "MB": return MB;            case "GB": return GB;            default: return 1;        }    }     public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        int n = Integer.parseInt(br.readLine().trim());        long total = 0;        for (int i = 0; i < n; i++) {            String[] parts = br.readLine().trim().split(" ");            total += Long.parseLong(parts[0]) * unit(parts[1]);        }         long blocks = 0;        if (total > ALLOWANCE) {            blocks = (total - ALLOWANCE + BLOCK - 1) / BLOCK; // 올림        }        System.out.println(total);        System.out.println(blocks);        System.out.println(blocks * PRICE);    }}
C#Main.cs · 43 行
using System; class Program{    // const 상수: 컴파일할 때 값이 정해진다    const long KB = 1024L;    const long MB = 1024 * KB;    const long GB = 1024 * MB;    const long ALLOWANCE = 3 * GB + 500 * MB; // 기본으로 주는 양    const long BLOCK = 100 * MB;              // 넘은 양을 세는 묶음    const long PRICE = 550;                   // 묶음 하나의 요금(원)     static long Unit(string u)    {        switch (u)        {            case "KB": return KB;            case "MB": return MB;            case "GB": return GB;            default: return 1;        }    }     static void Main()    {        int n = int.Parse(Console.ReadLine().Trim());        long total = 0;        for (int i = 0; i < n; i++)        {            string[] parts = Console.ReadLine().Trim().Split(' ');            total += long.Parse(parts[0]) * Unit(parts[1]);        }         long blocks = 0;        if (total > ALLOWANCE)        {            blocks = (total - ALLOWANCE + BLOCK - 1) / BLOCK; // 올림        }        Console.WriteLine(total);        Console.WriteLine(blocks);        Console.WriteLine(blocks * PRICE);    }}
C++main.cpp · 34 行
#include <iostream>#include <string>using namespace std; // constexpr 상수: 컴파일할 때 계산되고, 넘치면 컴파일 오류다constexpr long long KB = 1024;constexpr long long MB = 1024 * KB;constexpr long long GB = 1024 * MB;constexpr long long ALLOWANCE = 3 * GB + 500 * MB; // 기본으로 주는 양constexpr long long BLOCK = 100 * MB;              // 넘은 양을 세는 묶음constexpr long long PRICE = 550;                   // 묶음 하나의 요금(원) int main() {    int n;    cin >> n;    long long total = 0;    for (int i = 0; i < n; i++) {        long long a;        string u;        cin >> a >> u;        long long unit = 1;        if (u == "KB") unit = KB;        else if (u == "MB") unit = MB;        else if (u == "GB") unit = GB;        total += a * unit;    }     long long blocks = 0;    if (total > ALLOWANCE) {        blocks = (total - ALLOWANCE + BLOCK - 1) / BLOCK; // 올림    }    cout << total << '\n' << blocks << '\n' << blocks * PRICE << '\n';    return 0;}
Cmain.c · 33 行
#include <stdio.h>#include <string.h> /* #define은 글자를 그대로 바꿔 끼운다: 식은 꼭 괄호로 싼다 */#define KB 1024LL#define MB (1024 * KB)#define GB (1024 * MB)#define ALLOWANCE (3 * GB + 500 * MB) /* 기본으로 주는 양 */#define BLOCK (100 * MB)              /* 넘은 양을 세는 묶음 */#define PRICE 550                     /* 묶음 하나의 요금(원) */ int main(void) {    int n;    if (scanf("%d", &n) != 1) return 1;    long long total = 0;    for (int i = 0; i < n; i++) {        long long a;        char u[4];        if (scanf("%lld %3s", &a, u) != 2) return 1;        long long unit = 1;        if (strcmp(u, "KB") == 0) unit = KB;        else if (strcmp(u, "MB") == 0) unit = MB;        else if (strcmp(u, "GB") == 0) unit = GB;        total += a * unit;    }     long long blocks = 0;    if (total > ALLOWANCE) {        blocks = (total - ALLOWANCE + BLOCK - 1) / BLOCK; /* 올림 */    }    printf("%lld\n%lld\n%lld\n", total, blocks, blocks * PRICE);    return 0;}
Craykonmain.cray · 38 行
// 크레이콘에는 상수가 없다: 바꾸지 않기로 약속한 변수로 둔다킬로 = 1024;메가 = 1024 * 킬로;기가 = 1024 * 메가;기본량 = 3 * 기가 + 500 * 메가; // 기본으로 주는 양묶음 = 100 * 메가; // 넘은 양을 세는 묶음묶음요금 = 550; // 묶음 하나의 요금(원) 기록수 = 控制台.读行().转数字();합 = 0;循环 (i = 0; i < 기록수; i++){    칸들 = 控制台.读行().去空白().分割();    양 = 칸들[0].转数字();    단위 = 1;    如果 (칸들[1] == "KB")    {        단위 = 킬로;    }    否则 如果 (칸들[1] == "MB")    {        단위 = 메가;    }    否则 如果 (칸들[1] == "GB")    {        단위 = 기가;    }    합 += 양 * 단위;} 묶음수 = 0;如果 (합 > 기본량){    묶음수 = 数学.商(합 - 기본량 + 묶음 - 1, 묶음); // 올림}控制台.输出(합);控制台.输出(묶음수);控制台.输出(묶음수 * 묶음요금);