n辺の長さを与え、q回尋ね、形成可能な三角形の最大周囲長に関する各質問に答えます。
nとqはどちらも1 e 5 1e51 e 5の範囲。
最初に、配列の最大の周辺三角形を見つける方法について考えます。
順番に考えるのは簡単で、大きいものから小さいものまでN-2回列挙し、3つの辺が毎回三角形を形成できるかどうかを確認します。そうでない場合、最大の辺には、グループ化できる他の辺がありません(他の辺)。小さいです)。最初の実行可能なものが見つかった場合、それは最大の円周を持つ三角形の3つの辺です。
上記のアルゴリズムはO(N log N)O(NlogN)です。O (N l o g N )だけでなく、複数の質問の問題を直接解決するために直接使用される並べ替えも明らかに良くありません。複雑さはO(QN log N)O(QNlogN)です。O (Q N l o g N )。
大から小への列挙では、議長ツリーを使用して各O(ログN)O(ログN)を維持することを考えるのは簡単ですO (l o g N )はk番目の最大値を取得しますが、複雑さはO(QN log N)O(QNlogN)のままです。O (Q N l o g N )。
しかし実際には、結果を知るためにN回列挙する必要はありません。
ここで紹介する必要があります
insight
非常に長い配列があり、3辺が三角形を形成できない場合、1つの結論はフィボナッチ数列(各項目は最初の2つの項目の合計に等しい)であるということです。ただし、フィボナッチ数列は非常に速く成長し、45位で1e 9 1e9を上回っています。1 e 9。
したがって、1e 9 1e9の範囲のセットを指定します1 e 9の数が44を超える場合、フィボナッチ数列を壊す数が必要です。これは、三角形を形成できる3つの辺の存在と同等です。
この質問では、区間内で最大の44の数値を降順で列挙し、三角形を形成できるかどうかを確認し、最大の三角形を見つける必要があります。そして、複雑さはo(44 Q log N)o(44QlogN)に減少します。o (4 4 Q l o g N )。
このようにすれば、議長の木の定数はまったく恐れません。(より速くしたい場合は、44の最大数を維持するように線分ツリーを手書きすることもできます...)
#define _debug(x) cerr<<#x<<" = "<<x<<endl
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
//const int MAXN = 3000 + 59;
const ll MOD = 998244353;
const int MAXN = 100015;
const int M = MAXN * 30;
int n, q, m, tot;
int a[MAXN], t[MAXN];
int T[MAXN], lson[M], rson[M], c[M];
void Init_hush() {
for (int i = 1; i <= n; i++)
t[i] = a[i];
sort(t + 1, t + 1 + n);
m = unique(t + 1, t + 1 + n) - t - 1;
}
int build(int l, int r) {
int root = tot++;
c[root] = 0;
if (l != r) {
int mid = (l + r) >> 1;
lson[root] = build(l, mid);
rson[root] = build(mid + 1, r);
}
return root;
}
int hush(int x) {
return lower_bound(t + 1, t + 1 + m, x) - t;
}
int update(int root, int pos, int val) {
int newroot = tot++, tmp = newroot;
c[newroot] = c[root] + val;
int l = 1, r = m;
while (l < r) {
int mid = (l + r) >> 1;
if (pos <= mid) {
lson[newroot] = tot++;
rson[newroot] = rson[root];
newroot = lson[newroot];
root = lson[root];
r = mid;
} else {
rson[newroot] = tot++;
lson[newroot] = lson[root];
newroot = rson[newroot];
root = rson[root];
l = mid + 1;
}
c[newroot] = c[root] + val;
}
return tmp;
}
int query(int left_root, int right_root, int k) {
int l = 1, r = m;
while (l < r) {
int mid = (l + r) >> 1;
if (c[lson[left_root]] - c[lson[right_root]] >= k) {
r = mid;
left_root = lson[left_root];
right_root = lson[right_root];
} else {
l = mid + 1;
k -= c[lson[left_root]] - c[lson[right_root]];
left_root = rson[left_root];
right_root = rson[right_root];
}
}
return l;
}
ll Seg_k(int l, int r, int k) {
return 1ll * t[query(T[l], T[r + 1], k)];
}
ll chek(int mid, int AskL, int AskR) {
ll e1 = Seg_k(AskL, AskR, mid);
ll e2 = Seg_k(AskL, AskR, mid - 1);
ll e3 = Seg_k(AskL, AskR, mid - 2);
if (e2 + e3 > e1) {
return e1 + e2 + e3;
} else {
return -1;
};
}
int main() {
int tmp = 0;
ll ans = 0;
while (scanf("%d%d", &n, &q) == 2) {
tot = 0;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
Init_hush();
T[n + 1] = build(1, m);
for (int i = n; i; i--) {
int pos = hush(a[i]);
T[i] = update(T[i + 1], pos, 1);
}
while (q--) {
int askl, askr;
scanf("%d%d", &askl, &askr);
int r = askr - askl + 1;
int l = max(3, r - 48);
ans = -1;
for (int i = r; i >= l; i--) {
ans = chek(i, askl, askr);
if (ans > 0)break;
}
printf("%lld\n", ans);
}
}
return 0;
}
/*
*/