首页 > 其他分享 >Kevin and Math Class

Kevin and Math Class

时间:2024-12-27 16:21:37浏览次数:7  
标签:ch min int Math Tree ceil Kevin Class dp

前言

因为这个东西才开的这个专题, 但是我现在还是不会做这道题

思路

你发现 \(b_i \geq 2\) , 那么至多取 \(\log a_i\) 次就可以清空, 那么答案就有上界在 \(63\) 左右

因为操作顺序对最终结果无影响, 你考虑枚举以每个 \(b_i\) 作为区间最小值对于 \(a\) 的影响, 然后你很快就会发现, 一定让区间 \([l, r]\) 最大更优, 进一步发现这就是一颗笛卡尔树

具体的, 对于小根笛卡尔树上的一个子树代表的区间, 我们发现这就是最优的区间使得 \(b_i\) 为区间最小值, 那你考虑就在这上面做操作

令 \(dp_{u, i}\) 表示在 \(u\) 子树上做 \(i\) 次操作, \(a_i\) 在区间上的最大值

显然的这涉及到了左右两颗子树的讨论

假设只有一个儿子, 那么显然的

\[dp_{u, i} \gets \max \left(dp_{son, i}, a_u \right) \\ dp_{u, i} \stackrel{\min}{\longleftarrow} \left\lfloor \frac{dp_{u, i - 1}}{b_u} \right\rfloor \]

两个儿子涉及到背包合并了, 怎么做?

\[dp_{u,i}\gets \min_{j+k=i}(\max (dp_{ls,j},dp_{rs,k})) \\ dp_{u, i} \stackrel{\min}{\longleftarrow} \left\lfloor \frac{dp_{u, i - 1}}{b_u} \right\rfloor \]

暴力合并达到了 \(\mathcal{O} (n \log^2 V)\) , 你发现需要 \(\rm{Kaka}\) , 考虑还有优化方法吗?

显然是有的, 叫做 \(\min-\max\) 卷积但是我不会, 反正这个卡卡能过, 不管了

实现

框架

最大的难点疑似是怎么向上取整?

看了一下 \(\rm{GPT}\) , 发现可以这样实现

\[\text{ceil}(a / b) = \left( \frac{a + b - 1}{b} \right) \]

, 那就这么写

代码

\(\color{#052242}{\rm{TLE}}\) 了, 但是正确性没问题不调了
这个题疑似卡了双 \(\log\)

#include <bits/stdc++.h>
#define int long long
const int MAXN = 2e5 + 20;
const int INF = 0x7fffffffffffffff;
const int MAXANS = 70, ANS = 63;
#define ceil(a, b) (a + b - 1) / b

namespace IO {
    inline int read() {
        int x = 0, f = 1;
        char ch = getchar();
        while (ch < '0' || ch > '9') {
            if (ch == '-') f = -1;
            ch = getchar();
        }
        while (ch >= '0' && ch <= '9') x = x * 10 + (ch - '0'), ch = getchar();
        return f * x;
    }
} using namespace IO;

int n;
int a[MAXN], b[MAXN];

/*笛卡尔树*/
struct Cartesian_Tree
{
    struct node {
        int ls, rs, fa; // 位置信息
        int pri, key; // 优先级 & 键值

        bool operator < (const node& a) {
            return key < a.key;
        }
    } Tree[MAXN];

    void clear() { for (int i = 0; i <= n; i++) Tree[i].fa = Tree[i].key = Tree[i].pri = Tree[i].ls = Tree[i].rs = 0; }

    int vis[MAXN];
    /*找到根节点*/
    int findroot() {
        memset(vis, false, sizeof(vis));
        for (int i = 1; i <= n; i++) vis[Tree[i].ls] = vis[Tree[i].rs] = true;
        for (int i = 1; i <= n; i++) if (!vis[i]) return i;
    }

    /*建树预处理*/
    void init() {
        for (int i = 1; i <= n; i++) Tree[i].pri = b[i], Tree[i].key = i;
        Tree[0].key = 0, Tree[0].pri = -INF;
    }

    /*建树*/
    void buildtree() {
        init(); std::stack<int> MS; MS.push(0);
        for (int i = 1; i <= n; i++) {
            int pos = MS.top();
            while (!MS.empty() && Tree[pos].pri > Tree[i].pri) {
                pos = Tree[MS.top()].fa; MS.pop(); }

            Tree[i].ls = Tree[pos].rs, Tree[Tree[i].ls].fa = i, Tree[pos].rs = i, Tree[i].fa = pos;
            MS.push(i);
        }
    }
} CT;

int f[MAXN][MAXANS]; // dp 状态
int fpre[MAXANS]; // 预处理儿子的最优状态
/*树形 dp 辅助函数*/
void dfs1(int u, int fa) {
    int val = a[u]; // 当前新增的点
    int sonnum = (CT.Tree[u].ls != 0) + (CT.Tree[u].rs != 0); // 儿子节点的数量
    int ls = CT.Tree[u].ls, rs = CT.Tree[u].rs;
    /*叶子结点*/
    if (!sonnum) {
        for (int i = 0; i <= ANS; i++) f[u][i] = val, val = ceil(val, b[u]);
        return;
    } else {
        if (CT.Tree[u].ls) dfs1(CT.Tree[u].ls, u);
        if (CT.Tree[u].rs) dfs1(CT.Tree[u].rs, u);
        if (sonnum == 1) {
            int son = (CT.Tree[u].ls ? CT.Tree[u].ls : CT.Tree[u].rs);
            for (int i = 0; i <= ANS; i++) {
                int ret = val;
                if (i >= 1) f[u][i] = std::min(f[u][i], ceil(f[u][i - 1], b[u]));
                for (int j = i; j >= 0; j--) {
                    f[u][i] = std::min(f[u][i], std::max(ret, f[son][j]));
                    ret = ceil(ret, b[u]);
                }
            }
        } else {
            for (int j = 0; j <= ANS; j++) fpre[j] = INF;
            for (int i = 0; i <= ANS; i++)
                for (int j = 0; j <= i; j++) {
                    int k = i - j; int ret = std::max(f[ls][j], f[rs][k]);
                    fpre[i] = std::min(fpre[i], std::max(ret, val));
                }
            for (int i = 0; i <= ANS; i++) {
                int ret = val;
                if (i >= 1) f[u][i] = std::min(f[u][i], ceil(f[u][i - 1], b[u]));
                for (int j = i; j >= 0; j--) {
                    f[u][i] = std::min(f[u][i], std::max(ret, fpre[j]));
                    ret = ceil(ret, b[u]);
                }
            }
        }
    }
}

/*树形 dp*/
void solve() {
    for (int i = 1; i <= n; i++) for (int j = 0; j <= ANS; j++) f[i][j] = INF;
    int root = CT.findroot();
    dfs1(root, -1);

    for (int i = 0; i <= ANS; i++) {
        if (f[root][i] <= 1) {
            printf("%lld\n", i); return; } }
}

signed main()
{
    int t; t = read();
    while (t--) {
        n = read();
        for (int i = 1; i <= n; i++) a[i] = read();
        for (int i = 1; i <= n; i++) b[i] = read();
        CT.clear(); CT.buildtree();

        solve();
    }

    return 0;
}

总结

答案上界较小时, 考虑按照操作次数递推

笛卡尔树解决一类 类 \(\rm{RMQ}\) 问题, 并可以求出特殊区间的包含关系

善于在树上做 \(\rm{dp}\) 操作

标签:ch,min,int,Math,Tree,ceil,Kevin,Class,dp
From: https://www.cnblogs.com/YzaCsp/p/18636016

相关文章

  • Java 并发编程:原子类(Atomic Classes)核心技术的深度解析
    Java并发编程:原子类(AtomicClasses)核心技术的深度解析在高并发场景下,线程安全是一个重要的话题。Atomic类通过高效的CAS(Compare-And-Swap)机制,为开发者提供了一种无需锁的线程安全解决方案。本篇文章将系统讲解Java原子类的核心概念、常用成员、使用方法以及实际应用。......
  • 写css,class层级过多会影响页面的渲染性能吗?
    CSS类(class)层级的深度本身通常不会直接影响页面的渲染性能。然而,有几个与CSS和类层级相关的问题可能会影响性能:选择器复杂性:如果你使用了非常复杂的选择器,特别是那些涉及到多个类和/或ID的选择器,浏览器在解析和应用这些样式时需要更多的计算资源。虽然现代浏览器的优化已经......
  • ES6中class继承为什么一定要写super()?super代表什么?
    在ES6中,class关键字用于定义一个类,而extends关键字则用于实现类之间的继承。当一个类继承自另一个类时,子类的构造函数中必须调用super()方法。这是因为super()实际上调用了父类的构造函数,以确保父类中的属性和方法能够被正确地初始化并继承到子类中。super关键字在类继承......
  • 请分析Math.ceil(null)的结果
    在JavaScript中,Math.ceil()函数是用来对一个数进行上舍入的,也就是取大于或等于一个给定数字的最小整数。但是,当你向Math.ceil()传递一个非数字类型的参数时,比如null,JavaScript会尝试将这个参数转换为一个数字。在JavaScript中,null被转换为数字时会变为0。这是因为null在JavaScrip......
  • 是谁造成了 NoClassDefFoundError?
    半夜睡得正香的时候,突然接到警告电话,于是翻起身就打卡电脑连上环境查看是什么情况?登录上之后发现有个微服务占用的句柄数量一直在持续上涨,最终导致了微服务内存溢出挂掉了。这个微服务在运行的过程中会建立SSH连接,且之前这个微服务已经遇到过很多次类似的情况了,因此第一反应是哪里......
  • 告警处理 Unresolved attribute reference 'status_code' for class 'object'
    代码中有如下告警:1. Unresolvedattributereference'status_code'forclass'object'这个错误通常出现在使用Python进行编程时,尤其是在使用类似于Django或Flask这样的Web框架时。它意味着你尝试在一个类的实例中访问一个不存在的属性status_code。在这个上下文中,'obje......
  • 哪里有 class 告诉我?
    说明本文中的JVM参数和代码在JDK8版本生效。哪里有用户类?用户类是由开发者和第三方定义的类,它是由应用程序类加载器加载的。Java程序可以通过CLASSPATH环境变量,JVM启动参数-cp或者-classpath指定用户需要加载的类的路径。这两个配置的优先级从低到高,后面的配置会......
  • 《 C++ 点滴漫谈: 十一 》C++ 面向对象的秘密武器:全面掌握 class 的超能力
    摘要在C++中,class是面向对象编程的核心,它将数据和操作数据的函数封装在一起,从而提高代码的可维护性和复用性。本文详细探讨了C++class关键字的各个方面,包括类的基本概念、成员与访问控制、构造函数与析构函数、继承与多态、内存管理等内容。通过分析class与struct......
  • 【Java教程】Day4-14 面向对象编程(OOP): Classpath详解与Jar包使用指南
    在Java编程中,我们经常接触到classpath这一概念。虽然很多文章讨论了如何设置classpath,但其中大部分并不完全准确。在这篇文章中,我们将深入探讨classpath的作用、如何正确配置它、以及如何使用jar包来管理Java项目。  1.什么是Classpath?Classpath是JVM(Java虚拟机)用来查找......
  • C#知识整理-类(Class)
    关键字:struct:结构体class:类interface:接口abstract:定义抽象类或抽象方法使用sealed:密封类,不可继承的类void:表示无返回值 抽象类(abstractclass)抽象类不能被实例化。抽象类的用途是提供一个可供多个派生类共享的通用基类定义。例如,类库可以定义一个抽象类,将其用作多个类......