[KTSC 2024 R1] 最大化平均值 题解

先考虑封闭序列的个数,发现只有 O(n) 个,可以建出小根笛卡尔树,以 (ai,i) 作为权值,于是相同的一定是 i,rsi,rsrsi,

考虑如果没有重复,询问相当于给出一棵树,每次询问 subtree(u),保留一个含 u 的联通块的最大平均值。

可以把每个节点用向量代替,若最终取 u 中节点,则一定要取 u,故可以不断把 u 和其子树中的最优向量合并直到 u 是最优的。

这个过程不能均摊,但可以用平衡树上二分维护,具体是把应该弹出的全部和到一起再与 vectoru 加起来之后插回去。

对于重复的,就是必须同时选,那相当于每次的向量为 iarsi=ai, 的相加即可。

其实可以看郑老师的讲课视频。

代码:

#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
posted @   SkyMaths  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗
点击右上角即可分享
微信分享提示