Codeforces Round 881 (Div. 3) F2. Omsk Metro (hard version)(倍增+最大子段和)

原题链接:F2. Omsk Metro (hard version)


题目大意:


最初开始时,你有一个根节点 1 1 1 且权值为 1 1 1

接下来会有 n n n 个操作,每次操作按照如下格式给出:

设操作开始前节点总数为 c n t cnt cnt

  • + + + v v v x x x ,增加一个编号为 c n t + 1 cnt+1 cnt+1 ,且权值为 x ∈ { − 1 , 1 } x\in\{-1,1\} x{1,1} 的节点,同时和节点 v v v 连上一条边(保证节点 v v v 存在)。
  • ? ? ? x x x y y y k k k ,询问从 x x x y y y 的路径上,是否可以从某个位置开始,选出一些连续节点,并且使得点权之和为 k k k

解题思路:


注意到形成的结构是一棵树,并且还有一个性质: x ∈ { − 1 , 1 } x\in\{-1,1\} x{1,1}

我们引入一个结论:如果一个区间的和 s u m ≥ 0 sum\geq 0 sum0 ,那么我们可以通过删除最左边或最右边的一些元素,获得所有在区间 [ 0 , s u m ] [0,sum] [0,sum] 内的值,同样的, s u m ≤ 0 sum \leq 0 sum0 也满足这个条件。

感性证明一下为什么是对的:

首先我们肯定要从序列的一边慢慢删数,否则就不连续了。

  • 下面只考虑 s u m ≥ 0 sum \geq 0 sum0 的情况, s u m ≤ 0 sum\leq0 sum0 的情况也同理。
  • 假设序列的元素全为 1 1 1 ,结论显然是正确的。
  • 假设序列包含至少一个 − 1 -1 1 ,又因为 s u m = a × ( − 1 ) + b × 1 sum = a \times(-1)+b\times1 sum=a×(1)+b×1 ,对于每一个 − 1 -1 1,我们一定都能和某一个正 1 1 1 抵消掉这个 − 1 -1 1 的贡献,即删除了这个 − 1 -1 1 再删除一个 1 1 1 ,会使得 s u m sum sum 值不变,我们发现 − 1 -1 1 的存在本质上是没有意义的,这样问题就转化成了序列元素全是 1 1 1 的情况了。

引入这个结论有什么用呢?

假设我们可以知道这个序列 最小的负值和 S 1 S_{1} S1 以及 最大的正值和 S 2 S_{2} S2 ,按照这个结论,我们一定能用 S 1 S_{1} S1 凑出 [ S 1 , 0 ] [S_{1}, 0] [S1,0] 的任意值,用 S 2 S_{2} S2 凑出 [ 0 , S 2 ] [0,S_{2}] [0,S2] 的任意值。

所以判断是否能选出一个子数组,使得和为 k k k ,即判断 S 1 ≤ k ≤ S 2 S_{1} \leq k \leq S_{2} S1kS2 即可。

回到这题上,能否在 x → y x \rightarrow y xy 路径形成的序列上凑成 k k k ,本质就转化成了求 x → y x \rightarrow y xy 路径的最小子段和,以及最大子段和,这是一个典型问题。

假设根为 1 1 1 ,我们要求 x → y x \rightarrow y xy 的路径。

l c a lca lca x , y x,y x,y 的最近公共祖先。

假设 l c a lca lca x , y x,y x,y 的其中一个,那么答案就直接是 x → y x \rightarrow y xy 路径上的最大 / / /小子段和。

否则就是 x → l c a x \rightarrow lca xlca l c a → y lca \rightarrow y lcay 的路径拼起来,考虑怎么拼起来。

我们维护 7 7 7 个信息:

  • 严格包含 区间最左 / / /右端点的最大子段和: m x l , m x r mxl,mxr mxl,mxr
  • 严格包含 区间最左 / / /右端点的最小子段和: m n l , m n r mnl,mnr mnl,mnr
  • 只看这个区间内部的最大 / / /小子段和: m x s u m , m n s u m mxsum,mnsum mxsum,mnsum
  • 区间的和: s u m sum sum

假设我们已经知道 a → b a \rightarrow b ab b → c b \rightarrow c bc 的信息,我们要怎么将这两个区间合并成 a → c a \rightarrow c ac 的信息:

注意,这里的 a → b a \rightarrow b ab 指的是 左闭右开区间 [ a , b ) [a,b) [a,b) ,转化为下标可以理解为闭区间 [ a , b − 1 ] [a,b-1] [a,b1]

在这里插入图片描述
下面只考虑最大子段和,最小子段和也是同理:

按照上图将 a → b a \rightarrow b ab 统称为左边, b → c b \rightarrow c bc 统称为右边。

  • m x s u m a → c = max ⁡ { m x s u m a → b , m x s u m b → c , m x r a → b + m x l b → c } mxsum_{a \rightarrow c}=\max\{mxsum_{a \rightarrow b},mxsum_{b \rightarrow c}, mxr_{a \rightarrow b}+mxl_{b \rightarrow c}\} mxsumac=max{mxsumab,mxsumbc,mxrab+mxlbc}
    a → c a \rightarrow c ac 的最大子段和 m x s u m mxsum mxsum ,要么是左边或者右边其一的最大子段和,要么是左边 m x r mxr mxr 和右边 m x l mxl mxl 拼起来的中间那一段的和)

  • m x r a → c = max ⁡ { m x r b → c , m x r a → b + s u m b → c } mxr_{a \rightarrow c}=\max\{mxr_{b \rightarrow c},mxr_{a \rightarrow b}+sum_{b \rightarrow c}\} mxrac=max{mxrbc,mxrab+sumbc}
    a → c a \rightarrow c ac 包含右端点的最大子段和 m x r mxr mxr,要么是右边的 m x r mxr mxr,要么是左边的 m x r mxr mxr 再加上右边一整段区间的和)

  • m x l a → c = max ⁡ { m x l b → c , s u m a → b + m a x l b → c } mxl_{a \rightarrow c}=\max\{mxl_{b \rightarrow c},sum_{a \rightarrow b}+maxl_{b \rightarrow c}\} mxlac=max{mxlbc,sumab+maxlbc}
    a → c a \rightarrow c ac 包含左端点的最大子段和 m x l mxl mxl,要么是左边的 m x l mxl mxl,要么是左边一整段区间的和再加上右边的 m x l mxl mxl

  • s u m a → c = s u m a → b + s u m b → c sum_{a \rightarrow c} = sum_{a \rightarrow b} + sum_{b \rightarrow c} sumac=sumab+sumbc
    a → c a \rightarrow c ac 的区间的和直接就是左边的和和右边的和相加)

这样,我们就可以将树的每一条链分段维护,然后对每一段进行如上的信息合并,就能够实时求出 a → b a \rightarrow b ab 这一整条链上的信息了。

对于这一题而言,我们可以用倍增表做到在线加点维护信息,在线询问。

注意到我们的信息是自底向上维护的,且询问区间是 [ x , y ) [x,y) [x,y) 的左闭右开区间,因此我们除了 l c a lca lca x , y x,y x,y 的其中一个的情况,我们询问的答案都是 [ x , l c a ) , [ y , l c a ) [x,lca),[y,lca) [x,lca),[y,lca) 的信息。

这时我们要翻转一下一个区间,变成 [ x , l c a ) + [ l c a ] + ( l c a , y ] [x,lca)+[lca]+(lca,y] [x,lca)+[lca]+(lca,y] 的信息,才是 [ x , y ] [x,y] [x,y] 的信息。

细节比较多,可以看代码注释。

时间复杂度: O ( n log ⁡ n ) O(n \log n) O(nlogn)

AC代码:


#include <bits/stdc++.h>
#define YES return void(cout << "Yes\n")
#define NO return void(cout << "No\n")
using namespace std;

using u64 = unsigned long long;
using PII = pair<int, int>;
using i64 = long long;

//这里是我们的信息结构体
struct Info {
    int mnl, mnr, mxl, mxr, sum;
    int mxsum, mnsum;
    Info() { mnl = mnr = mxl = mxr = sum = mxsum = mnsum = 0; };
    Info(int _) {
        //初始时全为 0
        mnl = mnr = mxl = mxr = sum = mxsum = mnsum = 0;
        //分类讨论点 u 权值为 > 0 和 < 0 的情况
        if (_ > 0) {
            mxl = mxr = sum = mxsum = _;
        } else {
            mnl = mnr = sum = mnsum = _;
        }
    }
    //维护最大子段和
    friend Info merge(const Info& a, const Info& b) {
        Info res;
        res.mxsum = max({ a.mxsum, b.mxsum, a.mxr + b.mxl });
        res.mnsum = min({ a.mnsum, b.mnsum, a.mnr + b.mnl });
        res.mxl = max(a.mxl, a.sum + b.mxl);
        res.mxr = max(b.mxr, a.mxr + b.sum);
        res.mnl = min(a.mnl, a.sum + b.mnl);
        res.mnr = min(b.mnr, a.mnr + b.sum);
        res.sum = a.sum + b.sum;
        return res;
    }
    //区间反转操作
    void reverse() {
        swap(mnl, mnr);
        swap(mxl, mxr);
    }
};

void solve() {

    int n;
    cin >> n;

    const int logn = __lg(n);
    vector<vector<int>> nxt(n + 2, vector<int>(logn + 1));
    vector<vector<Info>> pre(n + 2, vector<Info>(logn + 1));

    vector<int> dep(n + 2);
    vector<tuple<int, int, int>> ask;

    //这里懒得把代码改了,就离线了,本质上是可以做到在线的
    int Node = 1;
    pre[Node][0] = Info(1);
    char op; int u, v, k;
    for (int i = 1; i <= n; ++i) {
        cin >> op >> u >> v;
        if (op == '+') {
            nxt[++Node][0] = u;
            dep[Node] = dep[u] + 1;
            pre[Node][0] = Info(v);
        } else {
            cin >> k;
            ask.emplace_back(u, v, k);
        }
    }

    //对每个点跳 2^j 步的信息都做一个如上的合并
    //注意信息是 [x, y) 的信息
    for (int j = 1; j <= logn; ++j) {
        for (int i = 1; i <= Node; ++i) {
            nxt[i][j] = nxt[nxt[i][j - 1]][j - 1];
            pre[i][j] = merge(pre[i][j - 1], pre[nxt[i][j - 1]][j - 1]);
        }
    }

    //求 LCA 模板
    auto LCA = [&](int u, int v) {
        if (dep[u] < dep[v]) swap(u, v);
        for (int j = logn; j >= 0; --j) {
            if (dep[u] - (1LL << j) >= dep[v]) {
                u = nxt[u][j];
            }
        }
        if (u == v) return u;
        for (int j = logn; j >= 0; --j) {
            if (nxt[u][j] != nxt[v][j]) {
                u = nxt[u][j];
                v = nxt[v][j];
            }
        }
        return nxt[u][0];
    };

    //cal函数 就是通过倍增计算 [x, a) + [a, b) + ... + [z, lca) 的信息从而得到 [x, lca) 的信息
    //特别要注意的是如果 u == lca 会返回一个全为 0 的信息,不会对答案产生影响,具体看代码即可明白原因
    auto cal = [&](int u, int lca) {
        Info res;
        for (int j = logn; j >= 0; --j) {
            if (dep[u] - (1LL << j) >= dep[lca]) {
                res = merge(res, pre[u][j]);
                u = nxt[u][j];
            }
        }
        return res;
    };

    //处理每个离线出来的询问
    for (auto [u, v, k] : ask) {
        int lca = LCA(u, v);
        Info A = cal(u, lca), B = cal(v, lca);
        A = merge(A, pre[lca][0]); //将 [u, lca) + [lca, lca]
        A.reverse(); //翻转 [u, lca]
        Info T = merge(B, A); //合并 [v, lca) + [lca, u];
        if (T.mnsum <= k && k <= T.mxsum) {
            cout << "Yes\n";
        } else {
            cout << "No\n";
        }
    }
}

signed main() {

    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int t = 1; cin >> t;
    while (t--) solve();

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/411658.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

小型内衣裤洗衣机哪个牌子好?四大顶尖内衣洗衣机测评分享

要知道&#xff0c;内衣裤可能会残留我们身体分泌的尿液&#xff0c;或者是没有擦干净的便便&#xff0c;以及其他的一些分泌物&#xff0c;据科学家研究发现&#xff0c;内衣裤是含有很多细菌和病毒的地方&#xff0c;如果将内衣裤和衣服放在一起洗&#xff0c;导致这些细菌附…

golang学习1,dea的golang-1.22.0

参考&#xff1a;使用IDEA配置GO的开发环境备忘录-CSDN博客 1.下载All releases - The Go Programming Language (google.cn) 2.直接next 3.window环境变量配置 4.idea的go插件安装 5.新建go项目找不到jdk解决 https://blog.csdn.net/ouyang111222/article/details/1361657…

MySQL数据库集群技术主从复制 一主一从详细讲解

集群技术 集群概述 MySQL复制技术 集群目的 负载均衡 解决高并发 高可用HA 服务可用性 远程灾备 数据有效性 类型 一主一从 一主双从 双主双从 原理 概念 在主库上把数据更改&#xff08;DDL DML DCL&#xff09;记录到二进制日志&#xff08;Binary Log&#xff09;中…

AngularJS安装版本问题

一、安装 Angular CLI 脚手架安装命令&#xff1a; npm install -g angular/cli 在安装前请确保自己安装NodeJS环境版本为V18及以上&#xff0c;否则会因node版本问题导致项目无法正常运行。 脚手架安装后&#xff0c;已提示了当前node版本必须为18.13.0或大于20.9.0版本&…

Linux网卡安装好后自启动

Linux系统配置网卡Wifi博客 https://developer.aliyun.com/article/704878 1、插入网卡iwconfig&#xff0c;查看id是wlxec607385c827 2、创建一个脚本文件 创建一个脚本文件&#xff0c;比如 /usr/local/bin/start_wifi.sh&#xff0c;并添加以下内容&#xff0c;id请根据自…

多维时序 | Matlab实现GRU-MATT门控循环单元融合多头注意力多变量时间序列预测模型

多维时序 | Matlab实现GRU-MATT门控循环单元融合多头注意力多变量时间序列预测模型 目录 多维时序 | Matlab实现GRU-MATT门控循环单元融合多头注意力多变量时间序列预测模型预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.多维时序 | Matlab实现GRU-MATT门控循环单元融…

【Java核心能力】为什么不建议在高并发场景下使用 synchronized?

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…

关注国外开源技术文档的中文版的朋友进来看一下

是否还在为技术文档全是英文而烦恼&#xff1f; 是否还在为国外网站访问慢甚至需要翻墙才能查看上面的文档而烦恼&#xff1f; 是否还在为翻译文档晦涩难懂、无法和作者交流而烦恼&#xff1f; 我创建了一个分享技术文档的网站&#xff0c;还在逐步完善&#xff0c;小伙…

大型电商日志离线分析系统(一)

一、项目需求分析 某大型网站日志离线分析系统 1.1 概述 该部分的主要目标就是描述本次项目最终七个分析模块的页面展示。 1.2 工作流 在我们的demo展示中&#xff0c;我们使用jqueryecharts的方式调用程序后台提供的rest api接口&#xff0c;获取json数据&#xff0c;然后…

【PostgreSQL】Windows安装PostgreSQL数据库图文详细教程

Windows安装PostgreSQL数据库图文详细教程 一、前言二、PostgreSQL简介三、软件下载四、安装步骤4.1 安装向导4.2 选择安装目录4.3 选择组件4.4 选择数据存放目录4.5 选择密码4.6 选择端口号4.7 等待安装完成4.8 取消勾选&#xff0c;安装完成 五、启动教程5.1 搜索pgAdmin4&am…

9.网络游戏逆向分析与漏洞攻防-游戏网络架构逆向分析-接管游戏连接服务器的操作

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;游戏底层功能对接类GameProc的实现 码云地址&#xff08;master 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/titan 码云版本号&#xff1a;44c54d30370d3621c1e9ec3d7fa1e2a0…

五种多目标优化算法(MSSA、MOJS、NSWOA、MOPSO、MOAHA)性能对比(提供MATLAB代码)

一、5种多目标优化算法简介 多目标优化算法是用于解决具有多个目标函数的优化问题的一类算法。其求解过程可以分为以下几个步骤&#xff1a; 1. 定义问题&#xff1a;首先需要明确问题的目标函数和约束条件。多目标优化问题通常涉及多个目标函数&#xff0c;这些目标函数可能…

ONLYOFFICE 8.0版本深度测评:革新之作还是失望之作?

引言 &#xff1a; 随着办公自动化需求的不断升级&#xff0c;拥有一款功能全面、操作便捷的办公软件显得尤为重要。在这个背景下&#xff0c;ONLYOFFICE作为一款集文档编辑、表格处理、演示制作等多功能于一体的办公套件&#xff0c;吸引了众多用户的关注。近期&#xff0c;O…

IO进程线程day8作业

信号灯集二次函数封装 sem.c #include<myhead.h>union semun {int val; /* Value for SETVAL */struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* B…

校园微社区微信小程序源码/二手交易/兼职交友微信小程序源码

云开发校园微社区微信小程序开源源码&#xff0c;这是一款云开发校园微社区-二手交易_兼职_交友_项目微信小程序开源源码&#xff0c;可以给你提供快捷方便的校园生活&#xff0c;有很多有趣实用的板块和功能&#xff0c;如&#xff1a;闲置交易、表白交友、疑问互答、任务兼职…

读书笔记之《没有工作的世界》:AI是否会取代人类?

《没有工作的世界—如何应对科技性失业与财富不平等》作者是 [英]丹尼尔苏斯金德&#xff0c; 原作名: A WORLD WITHOUT WORK&#xff1a;Technology,Automation,and How We Should Respond 丹尼尔苏斯金德&#xff08;Daniel Susskind &#xff09;&#xff1a;曾任英国首相战…

jvm面试题目补充

jdk&jre Java程序设计语言、Java虚拟机、Java API类库这三部分统称为JDK&#xff08;Java Development Kit&#xff09;。 把Java API类库中的Java SE API子集 [1] 和Java虚拟机这两部分统称为JRE&#xff08;Java Runtime Environment&#xff09;&#xff0c;JRE是支持…

python学习笔记 - 标准库函数

概述 为了方便程序员快速编写Python脚本程序&#xff0c;Python提供了很多好用的功能模块&#xff0c;它们内置于Python系统&#xff0c;也称为内置函数(Built-in Functions&#xff0c;BlF)&#xff0c;Python 内置函数是 Python 解释器提供的一组函数&#xff0c;无需额外导…

网络层的DDoS攻击与应用层的DDoS攻击之间的区别

DDoS攻击&#xff08;即“分布是拒绝服务攻击”&#xff09;&#xff0c;是基于DoS的特殊形式的拒绝服务攻击&#xff0c;是一种分布式、协作的大规模攻击方式&#xff0c;主要瞄准一些企业或政府部门的网站发起攻击。根据攻击原理和方式的区别&#xff0c;可以把DDoS攻击分为两…

“一代传奇”宗庆后谢幕,500亿元饮料帝国迎交棒时刻

宗庆后同志逝世 2月25日&#xff0c;娃哈哈集团发布讣告&#xff0c;娃哈哈创始人、董事长宗庆后同志&#xff0c;因病医治无效&#xff0c;于 2024-02-25 10:30 逝世&#xff0c;享年 79 岁。 这是一位伟大的企业家。 伟大的地方不仅仅在于&#xff0c;宗庆后 42 岁开始白手起…