[KTSC 2024 R1] 最大化平均值 题解
先考虑封闭序列的个数,发现只有
考虑如果没有重复,询问相当于给出一棵树,每次询问
可以把每个节点用向量代替,若最终取
这个过程不能均摊,但可以用平衡树上二分维护,具体是把应该弹出的全部和到一起再与
对于重复的,就是必须同时选,那相当于每次的向量为
其实可以看郑老师的讲课视频。
代码:
#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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗