先考虑封闭序列的个数,发现只有 \(\mathcal O(n)\) 个,可以建出小根笛卡尔树,以 \((a_i, i)\) 作为权值,于是相同的一定是 \(i, rs_i, rs_{rs_i},\dots\)。
考虑如果没有重复,询问相当于给出一棵树,每次询问 \(subtree(u)\),保留一个含 \(u\) 的联通块的最大平均值。
可以把每个节点用向量代替,若最终取 \(u\) 中节点,则一定要取 \(u\),故可以不断把 \(u\) 和其子树中的最优向量合并直到 \(u\) 是最优的。
这个过程不能均摊,但可以用平衡树上二分维护,具体是把应该弹出的全部和到一起再与 \(vector_u\) 加起来之后插回去。
对于重复的,就是必须同时选,那相当于每次的向量为 \(i\) 和 \(a_{rs_i} = a_{i},\dots\) 的相加即可。
其实可以看郑老师的讲课视频。
代码:
#include<bits/stdc++.h>
#include"average.h"
#define LL long long
#define eb emplace_back
#define PII pair <LL, int>
using namespace std;
/*
start at 21:04
< 30 min
finish at 21:29
code from A_zjzj
by sxt
*/
const int N = 3e5 + 9;
mt19937 mtrnd(chrono::system_clock::now().time_since_epoch().count());
struct vec {
int x; LL y;
vec() = default;
vec(int a, LL b): x(a), y(b) {}
} ;
vec operator +(vec a, vec b) {
return vec(a.x + b.x, a.y + b.y);
}
bool operator <(vec a, vec b) {
return a.y * b.x < b.y * a.x;
}
struct node {
int ls, rs, rnd;
vec val, sum;
} t[N];
void pushup(int x) {
t[x].sum = t[t[x].ls].sum + t[x].val + t[t[x].rs].sum;
}
void split(int p, vec val, int &x, int &y) {
// 简单分裂,< 和 >=
if (!p) return x = y = 0, void();
if (t[p].val < val) y = p, split(t[p].ls, val, x, t[p].ls);
else x = p, split(t[p].rs, val, t[p].rs, y);
pushup(p);
}
void Split(int p, vec val, int &x, int &y) {
// 一个一个弹出+合并的分裂
if (!p) return x = y = 0, void();
if (t[p].val < val + t[t[p].ls].sum) // 假设左边全部加入,此时是不优
y = p, Split(t[p].ls, val, x, t[p].ls);
else x = p, Split(t[p].rs, val + t[t[p].ls].sum + t[p].val, t[p].rs, y);
pushup(p);
}
int merge(int x, int y) {
// 简单合并
if (!x || !y) return x | y;
if (t[x].rnd < t[y].rnd) {
t[x].rs = merge(t[x].rs, y);
return pushup(x), x;
} else {
t[y].ls = merge(x, t[y].ls);
return pushup(y), y;
}
}
int Merge(int x, int y) {
// 权值有重复的合并,比启发式少了常数
if (!x || !y) return x | y;
if (t[x].rnd > t[y].rnd) swap(x, y);
// x 作为根
int r1, r2; split(y, t[x].val, r1, r2);
t[x].ls = Merge(t[x].ls, r1);
t[x].rs = Merge(t[x].rs, r2);
return pushup(x), x;
}
int ls[N], rs[N], n, a[N], root[N];
array <long long, 2> ans[N];
void dfs(int u, int l, int r) {
auto run = [&](int x, int l, int r) {
if (!x) return ;
// 可能是相同的链的左子树,故用 Merge
dfs(x, l, r);
root[u] = Merge(root[u], root[x]);
} ;
run(ls[u], l, u);
vec w(0, 0);
for (int i = u; ; i = rs[i]) {
w = w + vec(1, a[i]);
if (!rs[i] || a[i] != a[rs[i]]) {
run(rs[i], i, r);
break;
} else run(ls[rs[i]], i, rs[i]);
}
int r1, r2;
Split(root[u], w, r1, r2); // 合并儿子中“过于优的”
t[u] = {0, 0, (int)mtrnd(), w + t[r1].sum, w + t[r1].sum};
root[u] = merge(u, r2);
Split(root[u], w = vec(2, a[l] + a[r]), r1, r2);
w = w + t[r1].sum, ans[u] = {w.y, w.x};
// 求 u 子树的答案
root[u] = merge(r1, r2);
}
void initialize(std::vector<int> A) {
n = A.size();
for (int i = 1; i <= n; ++i) a[i] = A[i - 1];
static int top, stk[N];
for (int i = 1; i <= n; ++i) {
for (; top && a[stk[top]] > a[i]; --top) ls[i] = stk[top];
rs[stk[top]] = i, stk[++top] = i;
// 这里是建出笛卡尔树,线段的节点编号为最左边的数的位置,相等的为一条 rs 链
}
dfs(rs[0], 0, n + 1);
}
std::array<long long, 2> maximum_average(int i, int j) {
if (a[++i] <= a[++j]) return ls[j] ? ans[ls[j]] : array <LL, 2> {a[i] + a[j], 2};
else return rs[i] ? ans[rs[i]] : array <LL, 2> {a[i] + a[j], 2};
}
//////////////////////////////
#ifdef DEBUG
#include "grader.cpp"
#endif
标签:KTSC,return,R1,val,rs,int,题解,ls,vec
From: https://www.cnblogs.com/SkyMaths/p/18514594