#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
// Hằng số cho kích thước khối (block size) của Mo's Algorithm
const int MAXN = 100005;
const int BLOCK_SIZE = 316; // Căn bậc hai của MAXN (khoảng sqrt(10^5))
// Biến toàn cục để lưu trữ dữ liệu và kết quả
int N, Q;
vector<int> a;
long long F_current = 0; // Giá trị F(l, r) hiện tại
vector<long long> results;
// Cấu trúc cho truy vấn
struct Query {
int l, r, index;
int block_l;
// Sắp xếp truy vấn theo Mo's Algorithm
bool operator<(const Query& other) const {
if (block_l != other.block_l) {
return block_l < other.block_l;
}
// Sắp xếp zigzag: nếu block chẵn -> r tăng dần, block lẻ -> r giảm dần
if (block_l % 2 == 0) {
return r < other.r;
}
return r > other.r;
}
};
// --- Hàm Cập Nhật Mo's Algorithm ---
/*
* Công thức rút gọn cuối cùng cần tính là:
* F(l, r) = sum_{j=l}^{r} (r - j + 1) * sum_{k=0}^{j-l} [a_j != a_{l+k}]
*
* Hàm cập nhật sẽ tập trung vào sự thay đổi của tổng khi l hoặc r thay đổi 1 đơn vị.
* Sự thay đổi của r tương đối dễ hơn vì chỉ thay đổi hệ số (r - j + 1) và thêm/bớt j = r hoặc j = r+1.
* Sự thay đổi của l là phức tạp nhất.
*/
// Mảng/Cấu trúc dữ liệu phụ trợ:
// Ta cần đếm số lần mà một cặp (a_j, a_{l+k}) xuất hiện,
// nhưng cách tính này vẫn khó vì l thay đổi.
// Thay vào đó, ta sử dụng công thức ban đầu để cập nhật:
// F(l, r) = sum_{x=l}^{r} sum_{y=x}^{r} d(a[x, y], a[l, r + y - x])
long long calculate_d(int x, int y, int l_base) {
long long dist = 0;
int len = y - x + 1;
int r_base = l_base + len - 1; // Chỉ số cuối của đoạn thứ hai
// So sánh (a_x, ..., a_y) với (a_{l_base}, ..., a_{r_base})
for (int i = 0; i < len; ++i) {
if (a[x + i] != a[l_base + i]) {
dist++;
}
}
return dist;
}
// Cập nhật khi r tăng
void add_r(int new_r, int l_current) {
// Thêm các cặp (x, new_r) với l_current <= x <= new_r
for (int x = l_current; x <= new_r; ++x) {
// Đoạn 1: a[x, new_r]
// Đoạn 2: a[l_current, new_r + new_r - x]
F_current += calculate_d(x, new_r, l_current);
}
}
// Cập nhật khi r giảm
void remove_r(int old_r, int l_current) {
// Xóa các cặp (x, old_r) với l_current <= x <= old_r
for (int x = l_current; x <= old_r; ++x) {
// Đoạn 1: a[x, old_r]
// Đoạn 2: a[l_current, old_r + old_r - x]
F_current -= calculate_d(x, old_r, l_current);
}
}
// Cập nhật khi l giảm (l_new = l_current - 1)
void add_l(int new_l, int r_current) {
// Việc này phức tạp: l thay đổi, làm thay đổi VẾ THỨ HAI của khoảng cách Hamming
// F(new_l, r_current) = F(l_current, r_current) + Delta
// Delta = (Tổng cho x = new_l) + (Thay đổi do l_base -> new_l cho mọi cặp (x, y) cũ)
// 1. Thêm các đóng góp mới từ x = new_l:
for (int y = new_l; y <= r_current; ++y) {
// Đoạn 1: a[new_l, y]
// Đoạn 2: a[new_l, r_current + y - new_l]
F_current += calculate_d(new_l, y, new_l);
}
// 2. Cập nhật các đóng góp cũ (x > new_l):
// Đối với mọi cặp (x, y) cũ (l_current <= x <= y <= r_current),
// d(a[x, y], a[l_current, ...]) -> d(a[x, y], a[new_l, ...])
// Đây là phần tốn kém nhất:
for (int x = new_l + 1; x <= r_current; ++x) {
for (int y = x; y <= r_current; ++y) {
// Trừ đi giá trị cũ
F_current -= calculate_d(x, y, new_l + 1); // l_current = new_l + 1
// Cộng vào giá trị mới
F_current += calculate_d(x, y, new_l);
}
}
// *Lưu ý: Phần cập nhật này có độ phức tạp O(N^2) cho mỗi lần di chuyển l,
// điều này sẽ làm Mo's Algorithm bị TLE. Cần một cách cập nhật O(N) hoặc tốt hơn.*
// Do đó, cách tiếp cận này chỉ mang tính định hướng.
// Cách tối ưu thực sự đòi hỏi phân tích sâu hơn công thức rút gọn và sử dụng cấu trúc dữ liệu.
}
// Cập nhật khi l tăng (l_new = l_current + 1)
void remove_l(int old_l, int r_current) {
// Ngược lại với add_l, cũng rất phức tạp
// 1. Xóa đóng góp từ x = old_l:
for (int y = old_l; y <= r_current; ++y) {
F_current -= calculate_d(old_l, y, old_l);
}
// 2. Cập nhật các đóng góp cũ (x > old_l):
// Đối với mọi cặp (x, y) cũ (old_l + 1 <= x <= y <= r_current),
// d(a[x, y], a[old_l, ...]) -> d(a[x, y], a[old_l + 1, ...])
for (int x = old_l + 1; x <= r_current; ++x) {
for (int y = x; y <= r_current; ++y) {
// Trừ đi giá trị cũ
F_current -= calculate_d(x, y, old_l);
// Cộng vào giá trị mới
F_current += calculate_d(x, y, old_l + 1);
}
}
}
// --- Hàm Chính ---
void solve() {
cin >> N >> Q;
a.resize(N + 1); // Dùng 1-based indexing
for (int i = 1; i <= N; ++i) {
cin >> a[i];
}
vector<Query> queries(Q);
for (int i = 0; i < Q; ++i) {
cin >> queries[i].l >> queries[i].r;
queries[i].index = i;
queries[i].block_l = queries[i].l / BLOCK_SIZE;
}
// 1. Sắp xếp các truy vấn
sort(queries.begin(), queries.end());
results.resize(Q);
int current_l = 1;
int current_r = 0;
// 2. Xử lý từng truy vấn
for (const auto& q : queries) {
// Di chuyển con trỏ r
while (current_r < q.r) {
current_r++;
add_r(current_r, current_l);
}
while (current_r > q.r) {
remove_r(current_r, current_l);
current_r--;
}
// Di chuyển con trỏ l
while (current_l < q.l) {
remove_l(current_l, current_r);
current_l++;
}
while (current_l > q.l) {
current_l--;
add_l(current_l, current_r);
}
results[q.index] = F_current;
}
// 3. In kết quả
for (long long res : results) {
cout << res << "\n";
}
}
int main() {
// // Tăng tốc độ I/O
// ios_base::sync_with_stdio(false);
// cin.tie(NULL);
solve();
return 0;
}