[KTSC 2024 R1] 最大化平均值 题解
先考虑封闭序列的个数,发现只有 \(\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
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号