# Hướng dẫn giải: I. Bài cuối cùng :::info **Ý tưởng cốt lõi:** Thay vì duyệt 2 vòng lặp lồng nhau gây quá thời gian (TLE), ta sẽ gom nhóm các phép tính và dùng mảng cộng dồn (Prefix Sum) để trả lời truy vấn trong $O(1)$. ::: ## 1. Phân tích từ ví dụ để đưa ra quy luật Hãy nhìn vào ví dụ của đề bài: Xét truy vấn từ $l = 2$ đến $r = 4$. Theo yêu cầu, ta phải tính tổng các tích: $S = a_2 \times a_3 + a_3 \times a_4 + a_2 \times a_4$ Nếu sắp xếp và gom nhóm các nhân tử chung lại, ta có thể tách biểu thức trên thành: $S = a_4 \times (a_2 + a_3) + a_3 \times (a_2)$ > **=> Suy ra quy luật tổng quát:** > Để tính tổng các tích trong một đoạn, ta lấy từng phần tử $a_i$ nhân với **tổng của tất cả các phần tử đứng ngay trước nó** (trong phạm vi đang xét). ## 2. Hình thành ý tưởng Mảng tiền tố Từ quy luật gom nhóm trên, rất tự nhiên để chúng ta nghĩ ra việc tạo một **mảng tiền tố lưu tổng các tích** (gọi là `pre2`). Việc này giúp ta tránh bị TLE như cách duyệt trâu hai vòng lặp lồng nhau. Mảng `pre2[i]` sẽ lưu kết quả của bài toán nếu xét từ phần tử đầu tiên ($1$) đến $i$. Công thức tính lúc khởi tạo: `pre2[i] = pre2[i-1] + a[i] * (Tổng từ a[1] đến a[i-1])` **Đoạn code minh họa bước khởi tạo:** ```cpp ll sum = 0; for(int i = 1; i <= n; i ++) { cin >> arr[i]; ... pre2[i] = pre2[i - 1] + arr[i] * sum; // Mảng lưu tổng tích sum += arr[i]; // Cập nhật tổng các số phía trước } ``` ## 3. Tại sao `pre2[r] - pre2[l - 1]` là chưa đủ? Thông thường với mảng cộng dồn, để lấy tổng trong đoạn $[l, r]$, ta hay dùng `pre[r] - pre[l-1]`. Tuy nhiên, với mảng tổng tích này thì **chưa đủ**. Hãy nhìn lại ví dụ $l = 2, r = 4$. Giả sử ta lấy `pre2[4] - pre2[1]`: Bản thân `pre2[4]` đang chứa: * $a_4 \times (a_1 + a_2 + a_3)$ * $a_3 \times (a_1 + a_2)$ * $a_2 \times (a_1)$ Trừ đi `pre2[1]` (bằng 0), biểu thức vẫn y nguyên như trên. Trong khi đó, đáp án thực sự của đoạn từ $2 \rightarrow 4$ chỉ là $a_4 \times (a_2 + a_3) + a_3 \times (a_2)$. Rõ ràng, kết quả đang bị **dư thừa** các phép nhân với $a_1$ (các phần tử đứng trước $l$). Ta cần loại bỏ thằng $a_1$ này ra khỏi tất cả các cụm. ## 4. Xử lý phần dư thừa bằng mảng cộng dồn thường Do đó, ta cần tạo thêm một mảng cộng dồn thường (gọi là `pre1`) lưu tổng các phần tử để tính phần bị dư. Nhìn lại phần bị dư ở trên: Ta có $a_4 \times a_1$ , $a_3 \times a_1$ và $a_2 \times a_1$. Rút nhân tử chung ra, phần dư chính là: $a_1 \times (a_2 + a_3 + a_4)$. :::success **Quy luật phần dư:** Phần dư luôn bằng: **(Tổng các phần tử từ 1 đến l-1)** nhân với **(Tổng các phần tử từ l đến r)**. Dùng mảng `pre1`, phần dư = `pre1[l - 1] * (pre1[r] - pre1[l - 1])` ::: **Hoàn thành vòng lặp khởi tạo ở phía trên** ```cpp ll sum = 0; for(int i = 1; i <= n; i ++) { cin >> arr[i]; // mảng cộng dồn mới được thêm vào pre1[i] = pre1[i - 1] + arr[i]; pre2[i] = pre2[i - 1] + arr[i] * sum; // Mảng lưu tổng tích sum += arr[i]; // Cập nhật tổng các số phía trước } ``` ## 5. Ví dụ chốt lại ($l = 3, r = 4$) Để hiểu rõ công thức cuối cùng, ta thử lấy $l = 3, r = 4$. Yêu cầu bài toán lúc này chỉ là tính: $a_3 \times a_4$. **Bước 1:** Lấy `pre2[4] - pre2[2]` * `pre2[4]` có: $a_4(a_1 + a_2 + a_3) + a_3(a_1 + a_2) + a_2(a_1)$ * `pre2[2]` có: $a_2(a_1)$ * Trừ đi ta còn: $a_4(a_1 + a_2 + a_3) + a_3(a_1 + a_2)$ **Bước 2:** Trừ đi phần dư thừa Phần dư thừa trong cụm trên là $a_4(a_1 + a_2)$ và $a_3(a_1 + a_2)$. Gom lại ta được phần dư = $(a_1 + a_2) \times (a_3 + a_4)$. Chính là: `pre1[2] * (pre1[4] - pre1[2])` Lấy biểu thức ở Bước 1 trừ đi Bước 2, ta sẽ triệt tiêu hết $a_1, a_2$, phần còn lại đúng bằng $a_3 \times a_4$. :::danger **Công thức tổng quát cuối cùng cho mọi truy vấn l, r:** `Đáp án = pre2[r] - pre2[l - 1] - (pre1[r] - pre1[l - 1]) * pre1[l - 1]` ::: **Phần Code giải quyết truy vấn** ```cpp for(int i = 1; i <= q; i ++) { int l, r; cin >> l >> r; ll ans = pre2[r] - pre2[l - 1] - (pre1[r] - pre1[l - 1]) * pre1[l - 1]; cout << ans << '\n'; } ```