27.CF1004F Sonya and Bitwise OR 区间合并线段树

个人Limitの线段树题单题解主目录:Limitの线段树题单 题解目录_HeartFireY的博客-CSDN博客

给定序列,要求支持操作: 1.单点修改 2.查询区间内按位或和至少为X的子区间数

考虑分治。现在需要计算跨越区间中点的左、右端点对数。记录以区间中点为一端的前后缀,搭配双指针就可以 O ( n ) O(n) O(n) 计算

洛谷传送门:CF1004F Sonya and Bitwise OR - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

CF传送门:F. Sonya and Bitwise OR (codeforces.com)

题目思路

如果要检查合法性的话,只需要维护 20 20 20棵线段树就能支持。但是本题要求计数,问题就麻烦了。

  • 假设只有一个全局询问,考虑分治。现在需要计算跨越区间中点的左、右端点对数。记录以区间中点为一端的前后缀,搭配双指针就可以 O ( n ) O(n) O(n) 计算。

我们可以发现,由于或运算的性质,不同的前缀/后缀区间或 至多只有 log ⁡ a \log a loga 种。

  • 对于每个区间,我们分别记录前缀、后缀或和及区间答案,上传时分别传递前缀后缀、更新前缀后缀。

  • 考虑如何区间合并,我们分别开 v e c t o r vector vector记录不同的前后缀或值,在合并左右区间时,对于前缀序列,插入右区间中所有与整个左区间或产生的不同的值,右区间类比这么处理。

  • 考虑合并时如何更新答案:计算符合条件的 L , R L, R L,R对数,我们设置两个指针分别指向左右区间的左区间首和右区间尾,然后对于每个左端点缩右端点,然后暴力计数即可。

  • 对于答案的计算,直接双指针扫一遍,枚举在带合并的两个区间内分别枚举 L , R L, R L,R,然后计算符合条件的 L , R L, R L,R个数,就能实现两个区间的答案合并。

Code

#include <bits/stdc++.h>
#pragma gcc optimize("O2")
#pragma g++ optimize("O2")
#define int long long
#define endl '\n'
using namespace std;const int N = 2e5 + 10, MOD = 1e9 + 7;
int a[N], n, m, x;namespace ffastIO {const int bufl = 1 << 15;char buf[bufl], *s = buf, *t = buf;inline int fetch() {if (s == t) { t = (s = buf) + fread(buf, 1, bufl, stdin); if (s == t) return EOF; }return *s++;}inline int read() {int a = 0, b = 1, c = fetch();while (!isdigit(c))b ^= c == '-', c = fetch();while (isdigit(c)) a = a * 10 + c - 48, c = fetch();return b ? a : -a;}
}using ffastIO::read;#define pii pair<int, int>namespace SegTree{#define ls (rt << 1)#define rs (rt << 1 | 1)#define lson ls, l, mid#define rson rs, mid + 1, rstruct Info{vector<pii> pre, suf;int ans, pl, pr;Info(): ans(0) { pre.clear(), suf.clear(); }void clear() { pre.clear(), suf.clear(), ans = 0; }void add_pre(int x, int y) { pre.emplace_back(pii{x, y}); }void add_suf(int x, int y) { suf.emplace_back(pii{x, y}); }Info operator+ (Info b) {if(!pre.size()) return b;if(!b.pre.size()) return *this;Info res;res.ans = this -> ans + b.ans;res.pre = this -> pre;res.pl = pl, res.pr = b.pr;for(auto now : b.pre){if((pre.back().first | now.first) != res.pre.back().first)res.add_pre(pre.back().first | now.first, now.second);}res.suf = b.suf;for(auto now : suf){if((b.suf.back().first | now.first) != res.suf.back().first)res.add_suf(b.suf.back().first | now.first, now.second);}for(int i = 0, j = b.pre.size() - 1; i < (int)suf.size(); i++){while(j >= 0 && (suf[i].first | b.pre[j].first) >= x) j--;if(j + 1 < (int)b.pre.size())res.ans += 1ll * (suf[i].second - (i + 1 < (int)suf.size() ? suf[i + 1].second : pl - 1)) * (b.pr - b.pre[j + 1].second + 1);}return res;}}tree[N << 2];void push_up(int rt){ tree[rt] = tree[ls] + tree[rs]; }void build(int rt, int l, int r){if(l == r){tree[rt].pl = tree[rt].pr = l;tree[rt].add_pre(a[l], l);tree[rt].add_suf(a[l], l);tree[rt].ans = (a[l] >= x);return;}int mid = l + r >> 1;build(lson), build(rson);push_up(rt);}void update(int rt, int l, int r, int pos, int val){if(l == r){tree[rt].clear();tree[rt].add_pre(val, pos);tree[rt].add_suf(val, pos);tree[rt].ans = (val >= x);return;}int mid = l + r >> 1;if(mid >= pos) update(lson, pos, val);else update(rson, pos, val);push_up(rt);}Info query(int rt, int l, int r, int L, int R){if(l >= L && r <= R) return tree[rt];int mid = l + r >> 1; Info ans;if(mid >= L) ans = ans + query(lson, L, R);if(mid < R) ans = ans + query(rson, L, R);return ans;}#undef ls#undef rs#undef lson#undef rson
}#define SEGRG 1, 1, ninline void solve(){n = read(), m = read(), x = read();for(int i = 1; i <= n; i++) a[i] = read();SegTree::build(1, 1, n);while(m--){int op = read(), l = read(), r = read();if(op == 1) SegTree::update(SEGRG, l, r);else cout << SegTree::query(SEGRG, l, r).ans << endl;}
}signed main(){solve();return 0;
}

27.CF1004F Sonya and Bitwise OR 区间合并线段树相关推荐

  1. 牛客 - 求函数(线段树+区间合并/线段树+矩阵维护)

    题目链接:点击查看 题目大意:现在有 n 个函数,每个函数都是诸如 f( x ) = k * x + b 的形式,只是每个函数的 k 和 b 都是相互独立的,现在给出两个操作: 1 pos k b:将 ...

  2. CF1004F Sonya and Bitwise OR

    CF1004F Sonya and Bitwise OR Solution 感觉比较套路. 序列的前缀ororor有一个性质:最多变换logloglog次. 所以直接建一个线段树,每个区间对于前缀.后 ...

  3. CF1004F Sonya and Bitwise OR(线段树平衡复杂度+or 前缀性质)

    CF1004F Sonya and Bitwise OR 有一个长度为 \(n\) 的数组 \(\{a\}\),有 \(m\) 次操作,又给定一个数 \(x\),有两类操作: 1 i y 将 \(a_ ...

  4. 山东理工大学第十二届ACM程序设计竞赛 - Cut the tree(树上启发式合并+线段树)

    题目链接:点击查看 题目大意:给一个具有 N 个节点的有根树,以 1 号节点为根,节点编号从 1 开始,点有点权.树的第 H 层权值为深度为 H 的所有点的点权之和.树的总权值为所有层权值的最大值.问 ...

  5. 线段树 ---- CF1004F Sonya and Bitwise OR(线段树上分治合并区间信息 + or 前缀和的log性质)

    题目链接 题目大意: 解题思路: 考虑只有一次询问时怎么做. 分治.每次考虑LLL位于左半边,RRR位于右半边的情况(也就是"跨过中点"的答案).再分别递归左.右两边.计算跨过中点 ...

  6. 强制在线带修区间LCM(线段树+质因子状压)

    题目链接:信息学奥赛比赛系统 | 强制在线带修区间LCM (qduoj.com) 先说点题外话,这个题目困扰了我一天多,刚ac,真的是太爽了,感觉从这里面学到的东西挺多的,所以就来分享一下. 题目的意 ...

  7. 51nod2626-未来常数【树上启发式合并,线段树】

    正题 题目链接:http://www.51nod.com/Challenge/Problem.html#problemId=2626 题目大意 给出nnn个点的一棵树,每个区间[l,r][l,r][l ...

  8. [UOJ #222][NOI2016]区间(线段树)

    Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就是使得存在一个 x ...

  9. 模版:线段树合并+线段树分裂

    文章目录 前言 合并 代码 分裂 代码 前言 话说天下之树,分久必合,合久必分 合并 所谓合并,就是把两个树合并 以把B树合并到A树为例 如果A没有该节点,改成B的该节点返回 如果B没有该节点,直接返 ...

最新文章

  1. php7和python3性能对比-Python 2.7与Python 3.7区别
  2. 多层陶瓷电容器用处_典型陶瓷电容的用途和作用
  3. latex精要(1)-安装与helloworld
  4. 数组名加取地址符的理解
  5. linux cron读哪个文件,linux-管理cron作业创建的日志文件
  6. Python测试开发django1.简介
  7. 软件定义存储的特征及如何工作
  8. MySQL索引类型总结和使用技巧以及注意事项
  9. ios 发送请求时按home_iPhone 是否能够升级至指定的 iOS 系统版本?
  10. Android github上的好的开源项目汇总
  11. 文件下载的java代码_文件下载java代码实现
  12. /usr/bin/ld: cannot find -lmysqlclient解决方法
  13. NOPI修改xlsx文件内容,无法正常打开,提示文件格式或文件扩展名无效
  14. JAVA实战小项目——图书馆管理系统
  15. wireshark提示未启动npf服务The NPF driver isn’t running You may have trouble capturing or listing interfaces
  16. pycharm插件 Eval Reset 安装方法
  17. Qt下实现录制麦克风声音
  18. DNSChanger卷土重来,家用路由器当心了
  19. 算法思想记录:给定一个整数数组 nums 和一个目标值 target
  20. linux查看断网日志命令,Linux Screen命令使程序远离断网影响(示例代码)

热门文章

  1. C语言函数——复数四则运算
  2. 经典EMC辐射整改案例-车载显示屏流媒体EMI辐射超标整改分享
  3. 应用ggplot2绘制病例分级统计地图
  4. 新学期,新FLAG | 从心出发
  5. 如何在Access2007中使用日期类型查询数据
  6. :UG模块功能介绍::
  7. Anroid手机apk安装
  8. Fluent求解器——空化模型
  9. [算法]给定一个矩阵m*n,从左上角开始每次只能向右或者向下走,最后到右下角的位置共有多少种路径
  10. 键盘按键事件 通过键盘上下左右按键移动界面上图标