{ }codelper
整数除法负数取余自定义排序P0363

按股份分账后剩下的1元

n 个人合伙开店,i 号的股份是 w_i。
把所有股份加起来的值记作 W。
店里每个月把当月的盈亏 A 元按股份的比例分配。
A 为负数表示这个月亏损,这时每个人的份额都不超过 0,意思是要拿出这么多钱。
第一行是 n 和月数 q,第二行是 w_1 到 w_n,接下来 q 行每行给出一个月的 A。
i 号的份额先定为不超过 A × w_i ÷ W 的最大整数。
例如 3.5 得 3,−3.5 得 −4,能整除时就是那个值本身。
这时 i 号被舍去的部分是 A × w_i − 份额 × W,这个值是 0 以上、小于 W 的整数。
这样定出的份额加起来比 A 少 r 元(0 ≤ r < n)。
剩下的 r 元,从被舍去的部分最大的人开始,给 r 个人每人再加 1 元。
被舍去的部分相同时,编号小的人先得。
对每个月,把 1 号到 n 号最终的份额用一个空格隔开输出在一行。
输出的份额加起来总是等于 A。

输入
1 ≤ n ≤ 1,000,1 ≤ q ≤ 20,1 ≤ w_i ≤ 1,000,000,−1,000,000,000 ≤ A ≤ 1,000,000,000。
都是整数,负数前面带 -
输出
每个月一行:n 个份额
样例 1 · stdin
3 2
1 1 1
100
-100
stdout
34 33 33
-33 -33 -34
样例 2 · stdin
3 5
5 3 2
7
-7
-6
0
1000000000
stdout
4 2 1
-4 -2 -1
-3 -2 -1
0 0 0
500000000 300000000 200000000
main.py
输入(stdin)

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

$ 就绪

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

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

整数除法遇到负数时往哪边取整,决定了答案。
计算 −7 ÷ 2 时,Python 的 -7 // 2 得 −4(向下取整),C、C++、Java、C# 的 -7 / 2 得 −3(向 0 截断)。
JS 和 TS 没有单独的整数除法,Math.floor(-7 / 2) 是 −4,Math.trunc(-7 / 2) 是 −3。
Craykon 的 数学.向下取整(-7 / 2) 是 −4,数学.商(-7, 2) 是 −3。
需要向下取整时,Java 用 Math.floorDiv(a, b),C、C++、C# 用 q = a / b; if (a % b < 0) q--; 来实现。
陷阱一:向 0 截断的话,亏损的月份里份额之和会比 A 大。
第一个样例的 −100,三个人各 −33 是 −99,剩下的钱成了 −1,没有人可以给。
向下取整则是各 −34 共 −102,剩下 2 元。
陷阱二:余数也不一样。
Python 的 -35 % 10 是 5,而 C、C++、Java、C#、JS、TS、Craykon 的 -35 % 10 是 −5。
用 % 求被舍去的部分会把 0 和负数混在一起,第二个样例的 −6 里,被舍去的部分为 0 的 1 号反而被选成最大的。
要用 Java 的 Math.floorMod,或者用 A × w − 份额 × W 来求。
陷阱三:A × w 最大到 10^15,C、C++、Java、C# 的 int 会溢出(第二个样例的最后一个月)。
要用 long long 或 long;C# 和 Java 溢出时不报错,只是得到错误的值。
JS、TS、Craykon 的数在 2^53(约 9 × 10^15)以内是精确的,可以直接算,在这个范围内 Math.floor(a / b) 也是精确的。
陷阱四:Python 里 int(A * w / W) 不是向下取整,而是向 0 截断(int(-3.5) 是 −3)。
要用 //。
陷阱五:被舍去的部分相同时编号小的人优先。
第一个样例的 100 里,那 1 元给 1 号。
Python sorted、JS 和 TS 的 sort、Java 对对象数组的 Arrays.sort、C# OrderByDescending、C++ stable_sort、Craykon 排序 都是稳定排序,只比较被舍去的部分也能保留输入顺序;但 C qsort、C++ std::sort、C# Array.Sort 不保证是稳定排序,必须连编号一起比较。

Pythonmain.py · 27 行
import sys  def split(amount, weights, total):    """amount를 weights 비율로 나눈 몫 목록. 내림으로 나누고 남은 돈은 버린 부분이 큰 사람부터 1씩 준다."""    shares = [amount * w // total for w in weights]  # //는 음수에서도 내림(-7 // 2 == -4)    cut = [amount * w - s * total for w, s in zip(weights, shares)]  # 0 이상 total 미만    left = amount - sum(shares)    order = sorted(range(len(weights)), key=lambda i: (-cut[i], i))    for i in order[:left]:        shares[i] += 1    return shares  def main():    data = sys.stdin.read().split()    n, q = int(data[0]), int(data[1])    weights = [int(x) for x in data[2:2 + n]]    total = sum(weights)    out = []    for t in range(q):        amount = int(data[2 + n + t])        out.append(" ".join(map(str, split(amount, weights, total))))    print("\n".join(out))  main()
JavaScriptmain.js · 21 行
const data = require("fs").readFileSync(0, "utf8").split(/\s+/).filter((x) => x !== "").map(Number); // amount를 weights 비율로 나눈 몫 배열. 내림으로 나누고 남은 돈은 버린 부분이 큰 사람부터 1씩 준다.function split(amount, weights, total) {  // Math.floor는 음수에서도 내림이다(Math.trunc는 0 쪽으로 버림). 곱은 10^15 이하라 정확하다  const shares = weights.map((w) => Math.floor((amount * w) / total));  const cut = weights.map((w, i) => amount * w - shares[i] * total); // 0 이상 total 미만  let left = amount;  for (const s of shares) left -= s;  const order = weights.map((_, i) => i).sort((a, b) => cut[b] - cut[a] || a - b);  for (let k = 0; k < left; k++) shares[order[k]] += 1;  return shares;} const n = data[0];const q = data[1];const weights = data.slice(2, 2 + n);const total = weights.reduce((a, b) => a + b, 0);const out = [];for (let t = 0; t < q; t++) out.push(split(data[2 + n + t], weights, total).join(" "));console.log(out.join("\n"));
TypeScriptmain.ts · 23 行
import { readFileSync } from "node:fs"; const data: number[] = readFileSync(0, "utf8").split(/\s+/).filter((x: string) => x !== "").map(Number); // amount를 weights 비율로 나눈 몫 배열. 내림으로 나누고 남은 돈은 버린 부분이 큰 사람부터 1씩 준다.function split(amount: number, weights: number[], total: number): number[] {  // Math.floor는 음수에서도 내림이다(Math.trunc는 0 쪽으로 버림). 곱은 10^15 이하라 정확하다  const shares: number[] = weights.map((w: number) => Math.floor((amount * w) / total));  const cut: number[] = weights.map((w: number, i: number) => amount * w - shares[i] * total); // 0 이상 total 미만  let left = amount;  for (const s of shares) left -= s;  const order: number[] = weights.map((_: number, i: number) => i).sort((a: number, b: number) => cut[b] - cut[a] || a - b);  for (let k = 0; k < left; k++) shares[order[k]] += 1;  return shares;} const n: number = data[0];const q: number = data[1];const weights: number[] = data.slice(2, 2 + n);const total: number = weights.reduce((a: number, b: number) => a + b, 0);const out: string[] = [];for (let t = 0; t < q; t++) out.push(split(data[2 + n + t], weights, total).join(" "));console.log(out.join("\n"));
JavaMain.java · 46 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.Arrays;import java.util.StringTokenizer; public class Main {    // amount를 weights 비율로 나눈 몫 배열. 내림으로 나누고 남은 돈은 버린 부분이 큰 사람부터 1씩 준다.    static long[] split(long amount, int[] weights, long total) {        int n = weights.length;        long[] shares = new long[n];        long[] cut = new long[n];        long left = amount;        for (int i = 0; i < n; i++) {            shares[i] = Math.floorDiv(amount * weights[i], total); // /는 0 쪽으로 버리고 floorDiv는 내림            cut[i] = Math.floorMod(amount * weights[i], total);    // %는 음수가 나올 수 있고 floorMod는 0 이상            left -= shares[i];        }        Integer[] order = new Integer[n];        for (int i = 0; i < n; i++) order[i] = i;        Arrays.sort(order, (a, b) -> cut[a] != cut[b] ? Long.compare(cut[b], cut[a]) : Integer.compare(a, b));        for (int k = 0; k < left; k++) shares[order[k]]++;        return shares;    }     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(in.readLine());        int n = Integer.parseInt(st.nextToken()), q = Integer.parseInt(st.nextToken());        int[] weights = new int[n];        long total = 0;        st = new StringTokenizer(in.readLine());        for (int i = 0; i < n; i++) {            weights[i] = Integer.parseInt(st.nextToken());            total += weights[i];        }        StringBuilder out = new StringBuilder();        for (int t = 0; t < q; t++) {            long amount = Long.parseLong(in.readLine().trim());            long[] shares = split(amount, weights, total);            for (int i = 0; i < n; i++) out.append(i > 0 ? " " : "").append(shares[i]);            out.append('\n');        }        System.out.print(out);    }}
C#Main.cs · 48 行
using System;using System.Linq;using System.Text; class Program{    // C#의 /는 0 쪽으로 버리므로, 나머지가 음수이면 1을 빼서 내림으로 만든다    static long FloorDiv(long a, long b)    {        long quot = a / b;        if (a % b < 0) quot--;        return quot;    }     // amount를 weights 비율로 나눈 몫 배열. 내림으로 나누고 남은 돈은 버린 부분이 큰 사람부터 1씩 준다.    static long[] Split(long amount, int[] weights, long total)    {        int n = weights.Length;        var shares = new long[n];        var cut = new long[n];        long left = amount;        for (int i = 0; i < n; i++)        {            shares[i] = FloorDiv(amount * weights[i], total);            cut[i] = amount * weights[i] - shares[i] * total; // 0 이상 total 미만            left -= shares[i];        }        // OrderByDescending은 안정 정렬이라 버린 부분이 같으면 번호가 작은 사람이 앞에 남는다        int[] order = Enumerable.Range(0, n).OrderByDescending(i => cut[i]).ToArray();        for (int k = 0; k < left; k++) shares[order[k]]++;        return shares;    }     static void Main()    {        string[] head = Console.ReadLine().Trim().Split(' ');        int n = int.Parse(head[0]), q = int.Parse(head[1]);        int[] weights = Console.ReadLine().Trim().Split(' ').Select(int.Parse).ToArray();        long total = weights.Sum(w => (long)w);        var sb = new StringBuilder();        for (int t = 0; t < q; t++)        {            long amount = long.Parse(Console.ReadLine().Trim());            sb.Append(string.Join(" ", Split(amount, weights, total))).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 49 行
#include <algorithm>#include <iostream>#include <numeric>#include <vector>using namespace std; // C++의 /는 0 쪽으로 버리므로, 나머지가 음수이면 1을 빼서 내림으로 만든다long long floorDiv(long long a, long long b) {    long long quot = a / b;    if (a % b < 0) quot--;    return quot;} // amount를 weights 비율로 나눈 몫 목록. 내림으로 나누고 남은 돈은 버린 부분이 큰 사람부터 1씩 준다.vector<long long> split(long long amount, const vector<int>& weights, long long total) {    int n = (int)weights.size();    vector<long long> shares(n), cut(n);    long long left = amount;    for (int i = 0; i < n; i++) {        shares[i] = floorDiv(amount * weights[i], total);        cut[i] = amount * weights[i] - shares[i] * total; // 0 이상 total 미만        left -= shares[i];    }    vector<int> order(n);    iota(order.begin(), order.end(), 0);    // stable_sort라서 버린 부분이 같으면 번호가 작은 사람이 앞에 남는다    stable_sort(order.begin(), order.end(), [&](int a, int b) { return cut[a] > cut[b]; });    for (int k = 0; k < left; k++) shares[order[k]]++;    return shares;} int main() {    int n, q;    cin >> n >> q;    vector<int> weights(n);    long long total = 0;    for (int& w : weights) {        cin >> w;        total += w;    }    for (int t = 0; t < q; t++) {        long long amount;        cin >> amount;        vector<long long> shares = split(amount, weights, total);        for (int i = 0; i < n; i++) cout << (i ? " " : "") << shares[i];        cout << "\n";    }    return 0;}
Cmain.c · 48 行
#include <stdio.h>#include <stdlib.h> #define MAXN 1000 static long long cut[MAXN]; /* C의 /는 0 쪽으로 버리므로, 나머지가 음수이면 1을 빼서 내림으로 만든다 */static long long floor_div(long long a, long long b) {    long long quot = a / b;    if (a % b < 0) quot--;    return quot;} /* 버린 부분이 큰 사람이 앞, 같으면 번호가 작은 사람이 앞(qsort는 안정 정렬이 보장되지 않아 번호까지 비교한다) */static int by_cut(const void *x, const void *y) {    int a = *(const int *)x, b = *(const int *)y;    if (cut[a] != cut[b]) return cut[a] > cut[b] ? -1 : 1;    return a - b;} int main(void) {    static int weights[MAXN], order[MAXN];    static long long shares[MAXN];    int n, q;    if (scanf("%d %d", &n, &q) != 2) return 0;    long long total = 0;    for (int i = 0; i < n; i++) {        if (scanf("%d", &weights[i]) != 1) return 0;        total += weights[i];    }    for (int t = 0; t < q; t++) {        long long amount;        if (scanf("%lld", &amount) != 1) return 0;        long long left = amount;        for (int i = 0; i < n; i++) {            shares[i] = floor_div(amount * weights[i], total);            cut[i] = amount * weights[i] - shares[i] * total; /* 0 이상 total 미만 */            left -= shares[i];            order[i] = i;        }        qsort(order, (size_t)n, sizeof order[0], by_cut);        for (int k = 0; k < left; k++) shares[order[k]]++;        for (int i = 0; i < n; i++) printf(i ? " %lld" : "%lld", shares[i]);        printf("\n");    }    return 0;}
Craykonmain.cray · 39 行
head = 控制台.读数字();n = head[0];q = head[1];weights = 控制台.读数字();total = 0;遍历 (w 在 weights){    total = total + w;}cut = 新列表(n, 0); // 버린 부분이 큰 사람이 앞. 정렬은 안정 정렬이라 같으면 번호가 작은 사람이 앞에 남는다byCut(a, b){    返回 cut[b] - cut[a];} 循环 (t = 0; t < q; t++){    amount = 控制台.读行().去空白().转数字();    shares = [];    order = [];    left = amount;    循环 (i = 0; i < n; i++)    {        // 수학.내림은 음수에서도 내림이다(수학.몫은 0 쪽으로 버림). 곱은 10^15 이하라 정확하다        share = 数学.向下取整(amount * weights[i] / total);        cut[i] = amount * weights[i] - share * total; // 0 이상 total 미만        left = left - share;        shares.添加(share);        order.添加(i);    }    order.排序(byCut);    循环 (k = 0; k < left; k++)    {        shares[order[k]] = shares[order[k]] + 1;    }    控制台.输出(shares.连接(" "));}