trang chủ / đề bài / diffap / lời giải

Cập nhật cấp số cộng trên đoạn

Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
Tác giả: huunguyenhuunguyen

Hướng tiếp cận

Bài toán yêu cầu cộng một cấp số cộng vào một đoạn của mảng nhiều lần, sau đó in mảng kết quả. Với mảng hiệu bậc một thông thường, ta chỉ xử lý được cập nhật hằng số trên đoạn (A[i] += v). Để xử lý cập nhật tuyến tính theo i, ta dùng mảng hiệu bậc hai: lấy hiệu một lần nữa từ mảng hiệu bậc một. Khi đó một cấp số cộng trên đoạn trở thành chỉ O(1) thao tác trên mảng hiệu bậc hai.

Nhận xét quan trọng

  1. Mảng hiệu bậc một B[i]=A[i]−A[i−1] (với A[0]=0). Cập nhật A[i] += a+(i−l)·d trên i∈[l,r] sẽ làm:
    • B[l] tăng thêm a (vì A[l] tăng thêm a trong khi A[l−1] không đổi).
    • Với l+1≤i≤r: B[i] tăng thêm d (vì hiệu của hai số hạng liên tiếp trong cấp số cộng là d).
    • B[r+1] giảm a+(r−l)·d (vì A[r+1] không đổi, còn A[r] tăng thêm a+(r−l)·d).
  2. Trên B, cập nhật "cộng d cho mọi i∈[l+1,r]" lại là một cập nhật hằng số trên đoạn — ta xử lý bằng mảng hiệu bậc hai C[i]=B[i]−B[i−1].
  3. Vậy mỗi thao tác chỉ tác động lên O(1) vị trí của C.

Thuật toán

Khởi tạo mảng C[1..n+2]=0. Với mỗi thao tác (l,r,a,d):

  1. C[l] +=a — báo cho B[l] tăng thêm a.
  2. C[l+1] +=d−a — báo cho B[l+1] tăng thêm d thay vì a (chênh lệch là d−a).
  3. C[r+1] −=d+(a+(r−l)·d), tức là trừ đi a+(r−l+1)·d — báo cho B[r+1] giảm a+(r−l)·d (so với B[r] vốn tăng d, chênh lệch là −(a+(r−l)·d)−d).
  4. C[r+2] +=a+(r−l)·d — báo cho B[r+2] trở về 0 (so với B[r+1]).

Sau khi xử lý hết q thao tác, lấy tổng tiền tố hai lần:

  • B[i]=B[i−1]+C[i] với i=1,…,n.
  • A[i]=A[i−1]+B[i] với i=1,…,n.

In ra A[1],A[2],…,A[n].

Vì sao bốn phép cộng là đúng? Khi lấy tổng tiền tố B=prefix(C):

  • B[l]=a (chỉ C[l] đóng góp).
  • B[l+1]=a+(d−a)=d.
  • B[i]=d với l+1≤i≤r (không có C nào khác trong đoạn này).
  • B[r+1]=d−(a+(r−l+1)·d)=−(a+(r−l)·d).
  • B[r+2]=−(a+(r−l)·d)+(a+(r−l)·d)=0.

Lấy tổng tiền tố thêm một lần ra A:

  • A[l]=a, A[l+1]=a+d, ..., A[r]=a+(r−l)·d — đúng cấp số cộng.
  • A[r+1]=a+(r−l)·d+B[r+1]=0, A[r+2]=0, ...

Đúng như mong đợi: chỉ đoạn [l,r] bị thay đổi và bị cộng đúng cấp số cộng.

Lưu ý tràn số: Giá trị a+(r−l)·d có thể lớn cỡ 109+2·105·109≈2·1014, và sau q cập nhật giá trị tích lũy có thể đến ~4·1019 — vượt khỏi long long. Tuy nhiên đề bài đã đảm bảo kết quả cuối nằm trong long long, nên với phép tính modulo 264 (đặc trưng của số có dấu trong C/C++) các kết quả trung gian "tràn" sẽ tự khử lẫn nhau khi tổng tiền tố và cho ra kết quả đúng. Để an toàn, ta dùng kiểu long long (hoặc unsigned long long) cho mảng C.

Độ phức tạp

  • Thời gian: O(n+q).
  • Bộ nhớ: O(n).

Bình luận

Không có bình luận tại thời điểm này.

gnatmake 12.2.0 a68g 3.1.2 nasm 2.16.1 as_x64 2.47 awk 1.3.4 gcc 16.2.0 dotnet 10.0.402 g++ 16.2.0 g++-themis 16.2.0 g++17 16.2.0 g++20 16.2.0 g++23 16.2.0 clang++ 22.1.8 dmd 2.113.0 dart 3.13.6 gforth 0.7.3 gfortran 15.3.0 go 1.27.3 groovyc 5.1.3 javac 26.0.2.1 node 26.12.1 julia 1.13.2 kotlinc 2.4.21 lean 4.35.0 sbcl 2.2.9 lua 5.5.1 nim 2.2.12 fpc 3.2.2 fpc-themis 3.2.2 perl 5.42.3 php 8.5.12 pike 8.0 swipl 9.0.4 pypy3 7.3.23 python3 3.15.1 racket 8.7 ruby 4.0.8 rustc 1.99.1 csc 5.3.0 ctoj-scratch 0.0.1 sed 4.9 swiftc 6.4.1 tclsh 8.6 bun 1.4.4 deno 2.9.8 v 0.5.2 zig 0.17.0