Codeforces Round 495 (Div. 2) F. Sonya and Bitwise OR(线段树)

news2024/11/16 1:00:02

原题链接:F. Sonya and Bitwise OR


题目大意:


给出一个长度为 n n n 的数组 a a a,并给出 m m m 次询问以及一个数字 x x x

每个询问形式如下给出:

  • 1 1 1 i i i y y y :将 a i a_{i} ai 位置的值更改为 y y y
  • 2 2 2 l l l r r r :询问在数组区间 [ l , r ] [l, r] [l,r] 内有多少个子区间 [ L , R ] [L,R] [L,R] 满足 l ≤ L ≤ R ≤ r l \leq L \leq R \leq r lLRr ,且 a L o r   a L + 1 o r   . . .   o r   a R − 1 o r   a R ≥ x a_{L} or \space a_{L+1} or \space ... \space or \space a_{R-1} or \space a_{R} \geq x aLor aL+1or ... or aR1or aRx(其中 o r or or 表示二进制下的 操作)。

解题思路:


乍一看好像没什么很好做的方法,先考虑最暴力的做法是怎么做。

显然,对每个询问,枚举左端点 L L L 暴力向右找右端点 R R R 统计的总复杂度为 O ( m n 2 ) O(mn^{2}) O(mn2)

显然,对右端点的查询,我们做一个二分的优化复杂度可以变成 O ( m n log ⁡ n ) O(mn \log n) O(mnlogn),足够好了,但仍然无法通过。

考虑进一步挖掘一下性质,我们可以发现或操作是单调递增的,即只要某一个位置从 0 0 0 变成 1 1 1 后,则单调保持不变。

进一步思考,因为值域 V ∈ [ 0 , 2 20 ) V \in [0,2^{20}) V[0,220),那么我们的前缀或最多会变化 O ( log ⁡ V ) O(\log V) O(logV) 次,即最多会有 20 20 20 个位置有用(在这次 o r or or 操作时,补上了某个位使 0 0 0 1 1 1)。

我们只需要记录这些存在变化的位置,定下 L L L 之后让 R R R 在这些变化后的位置暴力枚举即可,可知我们最多只会枚举 O ( log ⁡ V ) O(\log V) O(logV) 次。

那问题来了,我们要怎么高效获取某个区间内哪些位置出现变化从而减少复杂度呢?答案很显然,线段树。

线段树内每个节点维护一个 v e c t o r vector vector 保存这个区间内,前/后缀出现变化的位置和其下标。

考虑如何维护区间的 v e c t o r vector vector

  • 假设以及维护好了 [ l , m i d ] , [ m i d + 1 , r ] [l,mid],[mid+1,r] [l,mid],[mid+1,r] 的信息,现在将信息上推至 [ l , r ] [l,r] [l,r]
  • [ l , r ] [l,r] [l,r] 的前缀或信息即 [ l , m i d ] [l,mid] [l,mid] 的前缀或的信息再继续不断或上 [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 的前缀或信息,假设或的过程中发现某个位置的比特位出现变化,则 p u s h b a c k push_back pushback 其位置的值和下标进 v e c t o r vector vector 内,可知枚举的位置总个数不会超过 O ( l o g V ) O(log V) O(logV) 个。
  • [ l , r ] [l,r] [l,r] 的后缀或信息即 [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 的后缀或的信息再继续不断或上 [ l , m i d ] [l,mid] [l,mid] 的后缀或信息,假设或的过程中发现某个位置的比特位出现变化,则 p u s h b a c k push_back pushback 其位置的值和下标进 v e c t o r vector vector 内,可知枚举的位置总个数不会超过 O ( l o g V ) O(log V) O(logV) 个。

考虑答案如何获取,我们采用一个分治的思想:

在这里插入图片描述

  • 假设我们已经计算完了 [ l , m i d ] , [ m i d + 1 , r ] [l,mid],[mid+1,r] [l,mid],[mid+1,r] 的答案,现在将计算横跨 m i d mid mid [ l , r ] [l,r] [l,r] 的答案。
  • 我们已知 [ l , m i d ] [l,mid] [l,mid] 的后缀或数组,以及 [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 的前缀或数组 L L L
  • 我们考虑设置两个双指针 L , R L,R L,R ,一个放在 [ l , m i d ] [l,mid] [l,mid] 的后缀数组 A A A 内,一个放在 [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 的前缀数组 B B B 内,那么显然我们可以枚举 L L L,然后寻找 R R R
  • 查询 [ L , R ] [L,R] [L,R] 的区间或是否 ≥ x \geq x x,即查询是否存在 A [ L ] o r B [ R ] ≥ x A[L] or B[R] \geq x A[L]orB[R]x,满足条件时答案直接加上 r − B [ R ] . s e c o n d + 1 r-B[R].second+1 rB[R].second+1,同时左指针右移( B [ R ] . s e c o n d B[R].second B[R].second 对应原数组内的即下标)。
  • 否则 < x < x <x 时右指针 R R R 向右移动直到满足答案为止。

这样,我们信息上推的复杂度就是 O ( log ⁡ n log ⁡ V ) O(\log n \log V) O(lognlogV) 的,单次查询也是 O ( log ⁡ n log ⁡ V ) O(\log n \log V) O(lognlogV) 的,修改直接照常修改即可,复杂度同时也是 O ( log ⁡ n log ⁡ V ) O(\log n \log V) O(lognlogV) 的。

时间复杂度: O ( m log ⁡ n log ⁡ V ) O(m \log n \log V) O(mlognlogV) ,可以通过,注意一些细节即可。

#include <bits/stdc++.h>
//using namespace std;

using PII = std::pair<int, int>;
using i64 = long long;

//线段树模板
template<class Info>
struct Segtree {
#define lson k << 1, l, mid
#define rson k << 1 | 1, mid + 1, r
    int n;
    std::vector<Info> info;
    Segtree(int _n) : n(_n), info((_n + 5) << 2) {};
    Segtree(std::vector<Info>& arr) : Segtree(arr.size() - 1) {
        std::function<void(int, int, int)> build = [&](int k, int l, int r) {
            if (l == r) {
                info[k] = arr[l];
                return;
            }
            int mid = l + r >> 1;
            build(lson), build(rson);
            pushup(k, l, r);
            };
        build(1, 1, n);
    }
    void pushup(int k, int l, int r) {
        info[k] = merge(info[k << 1], info[k << 1 | 1], l, r);
    }
    void Modify(int k, int l, int r, int x, const Info& z) {
        if (l == r) return void(info[k] = z);
        int mid = l + r >> 1;
        if (x <= mid) Modify(lson, x, z);
        if (x > mid) Modify(rson, x, z);
        pushup(k, l, r);
    }
    Info Query(int k, int l, int r, int x, int y) {
        if (l >= x && r <= y) return info[k];
        int mid = l + r >> 1;
        if (y <= mid) return Query(lson, x, y);
        if (x > mid) return Query(rson, x, y);
        return merge(Query(lson, x, y), Query(rson, x, y), std::max(l, x), std::min(y, r));
    }
    void Modify(int pos, const Info& z) {
        Modify(1, 1, n, pos, z);
    }
    Info Query(int l, int r) {
        return Query(1, 1, n, l, r);
    }
};

int X;

//注意这里的信息维护和答案上推操作
struct Info {
    i64 sum;
    std::vector<PII> L, R;
    friend Info merge(const Info& a, const Info& b, int QL, int QR) {
        Info res;
        res = { a.sum + b.sum, a.L, b.R };

        //R即 [l,mid] 的后缀或数组,L即[mid+1,r]的前缀或数组
        auto& R = a.R, & L = b.L;
        //这里的不同点是从大到小枚举 [mid+1,r] 的后缀或数组,同时查询 [l,mid] 的前缀或数组是否存在合法答案
        for (int l = R.size() - 1, r = 0; r < L.size(); ++r) {
            while (l > 0 && (R[l - 1].first | L[r].first) >= X) {
                --l;
            }
            //当答案满足时注意统计信息
            if ((R[l].first | L[r].first) >= X) {
                int Rlen = (r + 1 >= L.size() ? QR + 1 : L[r + 1].second) - L[r].second;
                int Llen = R[l].second - QL + 1;
                res.sum += 1LL * Rlen * Llen;
            }
        }
        //同时将维护的信息进行上推
        //保证[l,mid]的前缀或数组不变,枚举[mid+1,r]的前缀或数组上推 [l,r] 的前缀或数组
        for (auto& [v, p] : b.L) {
            auto& [x, _] = res.L.back();
            if ((x | v) > x) {
                res.L.emplace_back(x | v, p);
            }
        }
        //同时将维护的信息进行上推
        //保证[mid+1,r]的后缀或数组不变,枚举[l,mid]的后缀或数组上推 [l,r] 的后缀或数组
        for (auto& [v, p] : a.R) {
            auto& [x, _] = res.R.back();
            if ((x | v) > x) {
                res.R.emplace_back(x | v, p);
            }
        }
        //返回信息
        return res;
    }
};

void solve() {
    int n, q;
    std::cin >> n >> q >> X;

    std::vector<Info> A(n + 1);
    for (int i = 1; i <= n; ++i) {
        int val;
        std::cin >> val;
        A[i].L.emplace_back(val, i);
        A[i].R.emplace_back(val, i);
        A[i].sum = (val >= X);
    }

    Segtree<Info> S(A);
    for (int i = 1; i <= q; ++i) {
        int op, l, r;
        std::cin >> op >> l >> r;
        if (op == 1) {
            S.Modify(l, {(r >= X), {{r, l}}, {{r, l}} });
        } else {
            std::cout << S.Query(l, r).sum << "\n";
        }
    }
}

signed main() {

    std::ios::sync_with_stdio(0);
    std::cin.tie(0);

    int t = 1;
    //std::cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

将2,3,4,5,6,8分别填入算式“口口口X口口口“的“囗“中,怎么填使得算式结果最大。

一、解题思路 将数组元素进行全排列&#xff1a;对整个数组进行全排列&#xff0c;这样我们可以避免手动选择组合、排列剩余元素等步骤。 直接分割排列后的数组&#xff1a;在每一个全排列中&#xff0c;前3个元素和后3个元素自然形成了一个组合和一个剩余元素组合。 计算并…

Linux安装redis和使用redisDesktop连接

目录 Linux安装redis及启动 第一步&#xff1a;下载redis压缩包 第二步&#xff1a;下载gcc-c 第三步&#xff1a;解压redis文件 第四步&#xff1a;进入redis-4.0.0.0目录执行make命令 第五步&#xff1a;安装redis到redis目录 第五步&#xff1a;复制redis.conf配置文件…

电脑开机LOGO修改教程_BIOS启动图片替换方法

准备工具&#xff1a;刷BIOS神器和change logo&#xff0c;打包下载地址&#xff1a;https://download.csdn.net/download/baiseled/89374686 一.打开刷BIOS神器&#xff0c;点击备份BIOS&#xff0c;保存到桌面 二.打开change logo&#xff0c;1.点击load image&#xff0c;选…

11-sentinel利用nacos作持久化

本文介绍sentinel配置数据的持久化方法。由于sentinel官方并没有提供持久化功能&#xff0c;大家在测试过程中也能发现sentinel服务重启后&#xff0c;原来配置的数据就丢了&#xff0c;本文就是来处理这一问题的。 做好心理准备&#xff0c;我们要修改sentinel的源代码&#…

Python 批量修改 Word 文档中图片的大小并居中对齐

Python 批量修改 Word 文档中图片的大小并居中对齐 错过&#xff0c;再遇见可能就难了&#xff01;此时&#xff0c;你是你&#xff0c;我是我&#xff0c;再遇见&#xff0c;可就真的你是你&#xff0c;我是我&#xff0c;没有一丝的牵连纠缠—— !!! 对于已经编辑好的文档一定…

图表:调用FluentUI中的折线图散点图和饼状图

文章目录 0.文章介绍1.源码位置2.效果图3.代码3.1 代码结构3.2 main.qml3.3 MyLineChart.qml 0.文章介绍 调用项目FluentUI中的散点图、折线图和饼状图组件&#xff0c;做定制化改进。 项目FluentUI源码位置&#xff1a;https://github.com/zhuzichu520/FluentUI 项目FluentUI…

物联网产业链图谱_产业链全景图_物联网行业市场分析

物联网(IoT, Internet of Things)是通信网和互联网的拓展应用与网络延伸&#xff0c;它利用感知技术与智能装置对物理世界进行感知识别&#xff0c;通过网络传输互联&#xff0c;进行计算、处理和知识挖掘&#xff0c;实现人与物、物与物信息交互和无缝链接&#xff0c;达到对物…

springboot生成、响应图片验证码

我们平时经常会碰见图片验证码&#xff0c;那么在springboot中我们该怎么实现呢 我们可以使用一款开源的验证码生成工具EasyCaptcha&#xff0c;其支持多种类型的验证码&#xff0c;例如gif、中文、算术等&#xff0c;并且简单易用&#xff0c;具体内容可参考其官方文档。 效果…

网站SSL证书该如何更新?

网站SSL证书的更新是一个确保网站安全性的重要步骤。以下是一个详细的更新流程&#xff1a; 一、检查证书有效期 首先&#xff0c;需要定期检查SSL证书的有效期。通常情况下&#xff0c;SSL证书的有效期为一年&#xff0c;到期前需要进行更新。可以通过以下方式检查证书有效期…

证书|“机器学习工程师”来了,由工业和信息化部教育与考试中心颁发,含金量高

“机器学习工程师”职业技术考试是由工业和信息化部教育与考试中心推出人才考核标准&#xff0c;在互联网、零售、金融、电信、医学、旅游等行业均有涉及&#xff0c;是专门从事数据采集、数据分析、机器学习、人工智能并能制作业务报告、提供决策的新型数据分析人才所需要的技…

小程序滑动单元格

项目场景&#xff1a;小程序用户管理列表&#xff0c;通过单元格滑动实现“密码重置”、“删除”功能。 技术框架&#xff1a;uniapp、uview3、ts 效果如下&#xff1a; 前端页面&#xff1a; <template><view class"fui-wrap"><view class"f…

Ubuntu连接GitHub

报错&#xff1a;Please make sure you have the correct access rights and the repository exists.原因&#xff1a;本地没有SSH Key存在解决&#xff1a; 首先为系统设置github的用户名和自己的邮箱 git config --global user.name "****" git config --global us…

2024新型数字政府综合解决方案(一)

新型数字政府综合解决方案通过整合先进的数字技术和智能化系统&#xff0c;构建了一个高效、透明且响应迅速的政府服务平台&#xff0c;能够实现跨部门数据共享和实时信息更新。该解决方案包括智能数据分析、大数据平台和云计算服务&#xff0c;旨在提升政府决策的科学性和行政…

Transformer问题总结及实现

目录 前提&#xff1a; 注意&#xff1a;以下对于优化的问题&#xff0c;要回答这个问题&#xff1a;前一种方法的局限性在哪里&#xff0c;优化的方法是怎么进行优化的&#xff1f;&#xff08;未完全解决&#xff09; Step1:关于Transformer的疑问 Step2&#xff1a;关于…

初步认识Linux系统

前言 Linux系统具有许多优点&#xff0c;不仅系统性能稳定&#xff0c;而且是开源软件。其核心防火墙组件性能高效、配置简单&#xff0c;保证了系统的安全。在很多企业网络中&#xff0c;为了追求速度和安全&#xff0c;Linux不仅仅是被网络运维人员当作服务器使用&#xff0c…

FreeRTOS互斥量

文章目录 一、互斥量的使用场合二、互斥量函数1、创建2、其他函数 三、示例: 优先级继承四、递归锁1、死锁的概念2、自我死锁3、函数 怎么独享厕所&#xff1f;自己开门上锁&#xff0c;完事了自己开锁。 你当然可以进去后&#xff0c;让别人帮你把门&#xff1a;但是&#xff…

Linux驱动入门实验班——步进电机模块驱动(附百问网视频链接)

目录 一、工作原理 二、接口图 三、真值表 四、编写思路 1.构造file_operations结构体 2.编写入口函数 3.编写出口函数 4.编写write函数 五、bug记录 六、源码 课程链接 一、工作原理 步进电机由定子和转子两部分组成。定子上有多组线圈&#xff0c;通常称为相&…

8.16 mysql读写分离架构+MyCAT实现读写分离

1、读写分离的目的 数据库负载均衡&#xff1a; 当数据库请求增多时&#xff0c;单例数据库不能够满足业务 需求。需要进行数据库实例的扩容。多台数据库同时相 应请求。也就是说需要对数据库的请求&#xff0c;进行负载均衡 但是由于数据库服务特殊原因&#xff0c;数据库扩容…

《SPSS零基础入门教程》学习笔记——05.模型入门

文章目录 5.1 回归模型5.2 分类模型5.3 降维和刻度模型5.4 生存分析和时间序列模型 5.1 回归模型 线性回归&#xff08;分析 -> 回归 -> 线性&#xff09;曲线估计&#xff08;分析 -> 回归 -> 曲线估计&#xff09; 二元Logistic回归&#xff08;分析 -> 回归…