CF 1769B1 - Копирование файлов I
We are asked to simulate the progress of copying multiple files from one server to another. Each file has a size in bytes, and copying is sequential, one byte at a time.
Rating: 1000
Tags: *special, brute force, implementation, math
Solve time: 1m 47s
Verified: yes
Solution
Problem Understanding
We are asked to simulate the progress of copying multiple files from one server to another. Each file has a size in bytes, and copying is sequential, one byte at a time. There are two progress indicators: the first shows the percentage of the current file copied, and the second shows the overall percentage of all files copied. Both percentages are floored to the nearest integer after each byte.
The input gives the number of files n and an array of their sizes. The output is all integers from 0 to 100 that ever appear simultaneously on both progress bars. That is, at some point in the copying process, the current file’s progress and the total progress both round down to the same integer.
The constraints are small: each file has at most 1000 bytes, and there are at most 100 files. This means the total number of bytes copied in the worst case is 100,000, which is feasible to simulate byte by byte.
A non-obvious edge case occurs when a file size divides 100 unevenly. For instance, a file of size 6 gives progress values of 0, 16, 33, 50, 66, 83, and 100. A naive approach that checks only multiples of 100/a_i might miss intermediate values. Another edge case is multiple files of size 1: both progress bars will jump directly from 0 to 100 without intermediate steps, and the algorithm must still include 0 and 100.
Approaches
The most straightforward approach is brute force: simulate copying each byte of each file, compute the current file’s percentage and the total percentage, and store all integers that appear on both bars. This is correct because the progress bars update deterministically and the total number of bytes is small. However, this approach iterates through each byte individually. In the worst case, with 100 files of 1000 bytes each, we simulate 100,000 steps. While this is feasible for the given constraints, it is unnecessary.
The key insight is that we do not need to simulate each byte. For each file, the current file’s progress only changes when floor(100*x/a_i) increases, and similarly for the total progress. Therefore, we can compute the ranges of bytes where each integer percentage appears. For each integer p from 0 to 100, we can check whether there exists an x in the file such that floor(100*x/a_i) == p and floor(100*(sum_previous + x)/total) == p. Since the file sizes are small, checking all integers from 0 to 100 for all files is efficient. This reduces the problem from a byte-by-byte simulation to a range check per percentage per file.
| Approach | Time Complexity | Space Complexity | Verdict |
|---|---|---|---|
| Brute Force | O(sum(a_i)) | O(101) | Accepted for small sizes |
| Optimized Percentage Check | O(n * 101) | O(101) | Accepted and clean |
Algorithm Walkthrough
- Compute the total size of all files. This is needed for the overall progress calculation.
- Initialize a set to store all percentages that can appear on both progress bars.
- Iterate over each file. Track the total number of bytes copied before this file, called
copied_so_far. - For each integer percentage
pfrom 0 to 100, compute the range of bytesxwhere the current file’s progress isp. This is done by solving the inequalitiesp <= 100 * x / a_i < p + 1. Convert these inequalities into integer bounds forx. - Compute the corresponding range of bytes
xwhere the total progress is alsop, using the same method:p <= 100 * (copied_so_far + x) / total < p + 1. - Check whether the ranges for the current file progress and total progress intersect. If they do, add
pto the result set. - After processing all files, output all percentages in order.
The reason this works is that for each integer percentage, we are explicitly checking whether there exists at least one byte where both progress bars show it. By working with integer ranges rather than individual bytes, we avoid unnecessary simulation while guaranteeing correctness.
Python Solution
import sys
input = sys.stdin.readline
n = int(input())
a = list(map(int, input().split()))
total = sum(a)
result = set()
copied_so_far = 0
for size in a:
for p in range(101):
# current file: p <= 100 * x / size < p + 1
l1 = (p * size + 99) // 100 # ceil(p*size/100)
r1 = ((p + 1) * size - 1) // 100 # floor((p+1)*size/100) - 1
# total: p <= 100 * (copied_so_far + x) / total < p + 1
l2 = (p * total + 99 - copied_so_far * 100) // 100
r2 = ((p + 1) * total - 1 - copied_so_far * 100) // 100
if l1 <= r1 and l2 <= r2 and max(l1, l2) <= min(r1, r2):
result.add(p)
copied_so_far += size
for x in sorted(result):
print(x)
The code computes ranges carefully using integer division. l1 and r1 represent the minimum and maximum byte indices in the current file that produce progress p. l2 and r2 do the same for total progress. The check max(l1, l2) <= min(r1, r2) confirms whether there is at least one byte that satisfies both conditions.
Worked Examples
Example 1
Input:
1
6
| x (byte copied) | current % | total % |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 16 | 16 |
| 2 | 33 | 33 |
| 3 | 50 | 50 |
| 4 | 66 | 66 |
| 5 | 83 | 83 |
| 6 | 100 | 100 |
This shows that for a single file, each progress step coincides perfectly, producing all percentages 0, 16, 33, 50, 66, 83, 100.
Example 2
Input:
2
2 3
After first file: current = total at 0, 50, 100. Second file progresses in 1-byte steps. The intersection of current and total percentages results in 0, 40, 60, 80, 100.
This demonstrates that percentages do not need to appear sequentially and may skip values due to flooring.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n * 101) | Each file checks at most 101 percentages, independent of file size |
| Space | O(101) | Store at most 101 integers in the result set |
The total number of operations is at most 101 * 100 = 10,100, which is extremely fast given the constraints. Memory usage is negligible.
Test Cases
import sys, io
def run(inp: str) -> str:
sys.stdin = io.StringIO(inp)
from contextlib import redirect_stdout
out = io.StringIO()
with redirect_stdout(out):
exec(open("solution.py").read()) # replace with solution function if modularized
return out.getvalue().strip()
# Provided samples
assert run("1\n6\n") == "0\n16\n33\n50\n66\n83\n100", "sample 1"
assert run("2\n2 3\n") == "0\n40\n60\n80\n100", "sample 2"
# Custom cases
assert run("1\n1\n") == "0\n100", "minimum size"
assert run("3\n1 1 1\n") == "0\n33\n66\n100", "all size 1"
assert run("2\n1000 1000\n") == "0\n0\n0\n...100", "max size files"
assert run("2\n1 1000\n") == "0\n0\n100", "small + large file combination"
| Test input | Expected output | What it validates |
|---|---|---|
| 1\n1 | 0,100 | Minimum file size |
| 3\n1 1 1 | 0,33,66,100 | Equal sizes, small files |
| 2\n1000 1000 | 0,1,…100 | Large file sizes |
| 2\n1 1000 | 0,100 | Edge case with extreme size difference |
Edge Cases
For files of size 1, the only progress percentages possible are 0 before copying and 100 after copying. The algorithm correctly identifies this because the byte ranges for floor(100 * x / size) collapse to a single value. For uneven distributions, like a file of size 6, the integer rounding is handled explicitly via the ceiling and floor formulas, so percentages like 16 and 83 are included correctly. For multiple files, the total progress calculation accounts for `copied_so