首页 > 其他分享 >[KTSC 2024 R1] 最大化平均值 题解

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

时间:2024-10-29 21:59:23浏览次数:6  
标签:KTSC return R1 val rs int 题解 ls vec

先考虑封闭序列的个数,发现只有 \(\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

相关文章

  • [题解][CSP-S2024]擂台游戏
    题意[CSP-S2024]擂台游戏(民间数据)题目描述小S想要举办一场擂台游戏,如果共有\(2^k\)名选手参加,那么游戏分为\(k\)轮进行:第一轮编号为\(1,2\)的选手进行一次对局,编号为\(3,4\)的选手进行一次对局,以此类推,编号为\(2^k-1,2^k\)的选手进行一次对局。第二轮在......
  • 跨域问题解决办法
            跨域问题在Web开发中是一个常见的问题,特别是在前后端分离的开发模式下。以下是一些解决跨域问题的办法:一、后端配置CORS(跨来源资源共享)CORS是一种机制,它使用额外的HTTP头来告诉浏览器一个网页的当前来源(域名、协议和端口)是否有权限访问另一个来源的资源。1......
  • 【OJ题解】C++ 把字符串转换成整数
    ......
  • 题解:P7306 [COCI2018-2019#1] Strah
    分享一个\(O(nm\logm)\)的方法。分析考虑每次在\(x\)轴上固定左端点,然后移动\(x\)轴上的右端点,并统计答案。考虑如何统计一个\(1\timesn\)的区域的贡献。显然长度为\(i\)的区间有\(n-i+1\)个,所以贡献即为:\[\begin{aligned}f(n)=&\sum^n_{i=1}i\left(n-i+1\ri......
  • 题解:P8245 [COCI2013-2014#3] PAROVI
    题意定义两个整数\(A,B\)之间的距离为这两个整数所有对应位上的数的差的绝对值之和,记为\(\operatorname{dist}(A,B)\)。特别地,如果\(A,B\)两数的位数不相同,则在位数较小的数前补足前导\(0\)。现在,给定两个整数\(L,R\),请你求出所有在区间\([L,R]\)内的整数对的距离和。......
  • [GXYCTF 2019]Ping Ping Ping 题解(多种解题方式)
    知识点:命令执行linux空格绕过反引号绕过      变量绕过         base64编码绕过打开页面提示"听说php可以执行系统函数?我来康康"然后输入框内提示输入bjut.edu.cn  输入之后回显信息,是ping这个网址的信息 输入127.0.0.1因为提示是命令......
  • Educational Codeforces Round 171 (Rated for Div. 2) 题解(A-C)
    EducationalCodeforcesRound171(RatedforDiv.2)题解(A-C)这场ABC全都犯病了(悲伤)目录EducationalCodeforcesRound171(RatedforDiv.2)题解(A-C)目录A.PerpendicularSegmentsB.BlackCellsC.ActionFiguresA.PerpendicularSegments大意给你一个......
  • [CodeForces] CF520 题解
    A.Pangram【题目大意】给定一个字符串,询问是否所有英文字符(a到z)都在这个字符串中出现过,不区分大小写。【解题思路】开个桶记录字符是否出现过,最后遍历桶,如果发现有一个没出现就输出NO,否则就输出YES。注意不区分大小写!B.TwoButtons【题目大意】给定两个正整数\(n\)......
  • Educational Codeforces Round 171 (Rated for Div. 2)题解记录
    比赛链接:https://codeforces.com/contest/2026A.PerpendicularSegments题目说了必定有答案,直接对角线即可#include<iostream>#include<queue>#include<map>#include<set>#include<vector>#include<algorithm>#include<deque>#include<......
  • [ARC186E] Missing Subsequence 题解
    Description给定一个整数序列\(\left(X_1,\ldots,X_M\right)\),其长度为\(M\),元素取值为\(1,\ldots,K\)。要求找出长度为\(N\)的序列\((A_1,\ldots,A_N)\)的数量,元素取值为\(1,\ldots,K\),并满足以下条件,结果取模\(998244353\):在所有长度为\(M\)的序列中,唯......