Description
給定一個長度為 \(n\) 的整數序列 \(a_1\sim a_n\),其中的元素兩兩互不相等。
有 \(q\) 個詢問,每個詢問給定一個區間 \([l,r]\),你要選擇三個下標 \(i,j,k\in[l,r]\) 滿足 \(i\neq j,j\neq k,k\neq i\),最大化 \((a_i\bmod a_j)+(a_j\bmod a_k)+(a_k\bmod a_i)\) 的值。
你只需要輸出這個最大值。
\(3\leq n\leq2\times10^6\),\(1\leq q\leq8\times10^5\),\(\text{op}\in\{0,1\}\),\(1\leq a_i\leq10^{18}\),\(a_1\sim a_n\) 互不相等,\(1\leq l\leq r\leq n\),\(r-l+1\geq3\)。
Solution
不妨設 \(a_x>a_y>a_z\),那麼對於 \((x,y,z)\) 只有兩種貢獻:\(a_x\bmod a_y+a_y\bmod a_z+a_z\) 和 \(a_x\bmod a_z+a_z+a_y\)。
對於一組詢問 \([l,r]\),有個結論是 \([l,r]\) 內的區間最大值和次大值都必須選。
-
證明
先把區間的數拿出來並排序,使得 \(a_1<a_2<\ldots<a_m\),則選擇 \((m-2,m-1,m)\) 可以得到一個答案下界為 \(a_{m-1}+a_{m-2}\)。
-
如果最終答案為 \(a_x\bmod a_y+a_y\bmod a_z+a_z\),由於 \(a_x\bmod a_y+a_y\bmod a_z+a_z\leq\min\{a_x,2\cdot a_y-1\}\),則 \(x\leq m-1\) 或 \(y\leq m-2\) 一定沒上面那個優,所以 \(x=m\) 且 \(y=m-1\)。
-
如果最終答案為 \(a_x\bmod a_z+a_z+a_y\),由於 \(a_x\bmod a_z+a_z+a_y\leq a_x+a_y\),當 \(x\neq m\) 一定達不到最優解,又因為 \(a_y\) 只出現了一次,所以 \(y\) 一定儘量取到 \(m-1\)。
-
於是 \(x\) 和 \(y\) 就固定了,設 \(F(x,l,r)\) 表示將 \([l,r]\) 中的 \(x\) 和把剩下的最大值去掉後的所有 \(k\),\(a_x\bmod a_k+a_k\) 的最大值。
那麼答案就是 \(\max\{F(x,l,r)+a_y,F(y,l,r)+a_x\bmod a_y\}\),由於 \(x,y\) 已經確定,所以我們只需要求出 \(F(x,l,r)\) 的值即可。
考慮將 \(F(x,l,r)\) 拆成 \(F(x,l,x-1)\) 和 \(F(x,x+1,r)\)。對於 \(F(x,l,x-1)\),讓 \(l\) 從 \(x\) 列舉到 \(1\) 可以得到一個 \(O(n^2)\) 的做法。
又有個結論是如果掃到了某個 \(l\),如果存在至少兩個 \(a_i>\frac{a_x}{2}\) 就可以停止掃描。
-
證明
不妨設這兩個數是 \(a_i,a_j\) 且 \(a_i<a_j\)。
-
如果 \(a_i>a_x\),與 \(x\) 為區間最大值/次大值矛盾,這個區間一定不會被詢問到。
-
如果 \(\frac{a_x}{2}<a_i<a_x\),則 \(a_i\bmod a_x+a_x=a_i\),已經到了最大值,前面的一定不會更優。
-
基於這個做法暴力列舉 \(l\) 可以做到 \(O(n\log V+q\log n)\),過不了。
還有個結論是掃描到 \(a_i\) 時,如果已經存在兩個數 \(\geq 2\cdot a_i\),則 \(a_i\) 就可以刪掉。
-
證明
如果 \(a_j\geq 2\cdot a_i\),則 \(a_x\bmod a_j+a_j\geq a_j\geq 2\cdot a_i>a_x\bmod a_i+a_i\),所以 \(a_i\) 一定不會對答案造成貢獻。
所以可以在從小到大列舉 \(x\) 的過程中,維護一個棧表示目前還沒刪掉的數和這些數的刪除標記。然後在掃描 \(l\) 的過程中,維護另一個標記表示 \(>\frac{a_x}{2}\) 的個數。如果當前 \(a_i\leq \frac{a_x}{2}\) 就將 \(i\) 的刪除標記加 \(1\)。否則將另一個標記加一,如果另一個標記到了 \(2\) 就停止掃描,並把棧裡面刪除標記為 \(2\) 的數刪掉,並將 \(x\) 加到棧裡。
容易證明上面那個做法的預處理複雜度為 \(O(n)\)。
時間複雜度:\(O(n+q\log n)\)。
Code
#include <bits/stdc++.h>
// #define int int64_t
const int kMaxN = 2e6 + 5;
int n, q, tp;
int64_t a[kMaxN];
std::vector<std::pair<int, int64_t>> vecl[kMaxN], vecr[kMaxN];
int get(int x, int y) { return a[x] > a[y] ? x : y; }
struct SGT {
int N, mx[kMaxN * 4];
void pushup(int x) {
mx[x] = get(mx[x << 1], mx[x << 1 | 1]);
}
void build(int n) {
for (N = 1; N <= n + 1; N <<= 1) {}
for (int i = N; i <= N + n; ++i) mx[i] = i - N;
for (int i = N - 1; i; --i) pushup(i);
}
int query(int l, int r) {
int ret = 0;
for (l += N - 1, r += N + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
if (~l & 1) ret = get(ret, mx[l ^ 1]);
if (r & 1) ret = get(ret, mx[r ^ 1]);
}
return ret;
}
} sgt;
void getl() {
static int stk[kMaxN] = {0}, cnt[kMaxN] = {0}, tmp[kMaxN];
int top = 0;
for (int i = 1; i <= n; ++i) {
int now = 0, mx = 0, cur = top + 1;
int64_t res = LLONG_MIN;
for (int j = top; j; --j) {
if (!mx) {
mx = stk[j];
} else if (a[stk[j]] < a[mx]) {
res = std::max(res, a[i] % a[stk[j]] + a[stk[j]]);
} else {
res = std::max(res, a[i] % a[mx] + a[mx]);
mx = stk[j];
}
vecl[i].emplace_back(stk[j], res);
if (a[stk[j]] > a[i] / 2) {
if (++now == 2) break;
} else {
++cnt[stk[j]];
}
cur = j;
}
int m = 0;
for (int j = cur; j <= top; ++j)
if (cnt[stk[j]] < 2)
tmp[++m] = stk[j];
top = cur - 1;
for (int j = 1; j <= m; ++j) stk[++top] = tmp[j];
stk[++top] = i;
std::reverse(vecl[i].begin(), vecl[i].end());
}
}
void getr() {
static int stk[kMaxN] = {0}, cnt[kMaxN] = {0}, tmp[kMaxN];
int top = 0;
for (int i = n; i; --i) {
int now = 0, mx = 0, cur = top + 1;
int64_t res = LLONG_MIN;
for (int j = top; j; --j) {
if (!mx) {
mx = stk[j];
} else if (a[stk[j]] < a[mx]) {
res = std::max(res, a[i] % a[stk[j]] + a[stk[j]]);
} else {
res = std::max(res, a[i] % a[mx] + a[mx]);
mx = stk[j];
}
vecr[i].emplace_back(stk[j], res);
if (a[stk[j]] > a[i] / 2) {
if (++now == 2) break;
} else {
++cnt[stk[j]];
}
cur = j;
}
int m = 0;
for (int j = cur; j <= top; ++j)
if (cnt[stk[j]] < 2)
tmp[++m] = stk[j];
top = cur - 1;
for (int j = 1; j <= m; ++j) stk[++top] = tmp[j];
stk[++top] = i;
std::reverse(vecr[i].begin(), vecr[i].end());
}
}
void prework() {
sgt.build(n);
getl(), getr();
}
int64_t F(int x, int l, int r, int mx) {
int64_t ret = LLONG_MIN;
int y = sgt.query(l, x - 1), z = sgt.query(x + 1, r);
if (y && y != mx) ret = std::max(ret, a[y] + a[x] % a[y]);
if (z && z != mx) ret = std::max(ret, a[z] + a[x] % a[z]);
auto it1 = std::lower_bound(vecl[x].begin(), vecl[x].end(), std::pair<int, int64_t>{l, LLONG_MIN});
auto it2 = std::lower_bound(vecr[x].begin(), vecr[x].end(), std::pair<int, int64_t>{r, LLONG_MAX}, std::greater<>());
if (it1 != vecl[x].end()) ret = std::max(ret, it1->second);
if (it2 != vecr[x].end()) ret = std::max(ret, it2->second);
return ret;
}
int64_t query(int l, int r) {
int x = sgt.query(l, r), y = get(sgt.query(l, x - 1), sgt.query(x + 1, r));
return std::max(F(x, l, r, y) + a[y], F(y, l, r, x) + a[x] % a[y]);
}
void dickdreamer() {
std::cin >> n >> q >> tp;
for (int i = 1; i <= n; ++i) std::cin >> a[i];
prework();
int64_t lastans = 0;
for (int i = 1; i <= q; ++i) {
int l, r;
std::cin >> l >> r;
l = (l + lastans * tp - 1) % n + 1;
r = (r + lastans * tp - 1) % n + 1;
std::cout << (lastans = query(l, r)) << '\n';
}
}
int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}