题面

Bzoj

洛谷

题解

考虑一个什么样的区间满足重组之后可以变成$3$的倍数。不妨设$tot$为一个区间内$1$的个数。如果$tot$是个偶数,则这个区间一定是$3$的倍数,接着考虑奇数的情况。

如果只有$1$个$1$,那么无论如何都不行,只需考虑$3$个$1$的情况,因为其他的$1$可以看做偶数个$1$的情况。不难发现,当只有$3$个$1$的时候,我们需要有至少$2$个$0$,接着就可以用线段树来维护了。

我们考虑记录三个数组,$sum[4][3], lx[4][3], rx[4][3]$,分别表示区间中的总的方案数,包含左端点的方案数,包含右端点的方案数。

而对于后面的二维数组,第一位表示包含$1$的个数,分别表示有零个$1$,一个$1$,有偶数(大于$0$)个$1$,有奇数(大于$1$)个一

二位表示$0$的个数,分别表示有零个$0$,一个$0$,以及两个及以上个$0$

比如说$sum[2][1]$就是表示,当前区间中,包含有偶数个$1$,并且有且仅有一个$0$的子区间的个数。

接着的难点就是写$pushup$了,可以参考代码。

#include <cstdio>
#include <cstring>
#include <algorithm>
using std::min; using std::max;
using std::sort; using std::swap;
typedef long long ll; template<typename T>
void read(T &x) {
int flag = 1; x = 0; char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') flag = -flag; ch = getchar(); }
while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); x *= flag;
} const int N = 1e5 + 10;
int n, m, x, l, r, opt, a[N];
inline int get1(int x) { return (x <= 1) ? x : (x % 2 + 2); }
inline int get0(int x) { return (x <= 1) ? x : 2; }
struct Matrix {
ll cnt[2], sum[4][3], lx[4][3], rx[4][3];
Matrix operator + (const Matrix & a) const {
Matrix ret, lc = *this, rc = a;
ret.cnt[0] = lc.cnt[0] + rc.cnt[0];
ret.cnt[1] = lc.cnt[1] + rc.cnt[1];
for(int i = 0; i < 4; ++i)
for(int j = 0; j < 3; ++j) {
ret.sum[i][j] = lc.sum[i][j] + rc.sum[i][j];
ret.lx[i][j] = lc.lx[i][j], ret.rx[i][j] = rc.rx[i][j];
}
for(int i1 = 0; i1 < 4; ++i1)
for(int i0 = 0; i0 < 3; ++i0)
if(lc.rx[i1][i0]) {
ll x = lc.rx[i1][i0];
for(int j1 = 0; j1 < 4; ++j1)
for(int j0 = 0; j0 < 3; ++j0)
if(rc.lx[j1][j0]) {
ll y = rc.lx[j1][j0];
ret.sum[get1(i1 + j1)][get0(i0 + j0)] += x * y;
}
}
int lc0 = lc.cnt[0], lc1 = lc.cnt[1];
int rc0 = rc.cnt[0], rc1 = rc.cnt[1];
for(int i = 0; i < 4; ++i)
for(int j = 0; j < 3; ++j) {
ret.lx[get1(lc1 + i)][get0(lc0 + j)] += rc.lx[i][j];
ret.rx[get1(rc1 + i)][get0(rc0 + j)] += lc.rx[i][j];
}
return ret;
}
} t[N << 2]; inline void pushup(int o, int lc, int rc) { t[o] = t[lc] + t[rc]; }
void build(int o = 1, int l = 1, int r = n) {
if(l == r) {
int x = (a[l] == 1), y = (a[l] == 0);
t[o].cnt[0] = y, t[o].cnt[1] = x;
t[o].sum[x][y] = t[o].lx[x][y] = t[o].rx[x][y] = 1;
return ;
} int mid = (l + r) >> 1, lc = o << 1, rc = lc | 1;
build(lc, l, mid), build(rc, mid + 1, r), pushup(o, lc, rc);
}
void modify(int k, int o = 1, int l = 1, int r = n) {
if(l == r) {
int x = t[o].cnt[1], y = t[o].cnt[0];
t[o].sum[x][y] = t[o].lx[x][y] = t[o].rx[x][y] = 0;
swap(t[o].cnt[0], t[o].cnt[1]);
t[o].sum[y][x] = t[o].lx[y][x] = t[o].rx[y][x] = 1;
return ;
} int mid = (l + r) >> 1, lc = o << 1, rc = lc | 1;
if(k <= mid) modify(k, lc, l, mid);
else modify(k, rc, mid + 1, r);
pushup(o, lc, rc);
}
Matrix query(int ql, int qr, int o = 1, int l = 1, int r = n) {
if(l >= ql && r <= qr) return t[o];
int mid = (l + r) >> 1, lc = o << 1, rc = lc | 1;
if(qr <= mid) return query(ql, qr, lc, l, mid);
else if(ql > mid) return query(ql, qr, rc, mid + 1, r);
else return query(ql, qr, lc, l, mid) + query(ql, qr, rc, mid + 1, r);
} int main () {
read(n);
for(int i = 1; i <= n; ++i) read(a[i]);
read(m), build(); Matrix ans; ll ret;
while(m--) {
read(opt);
if(opt == 1) read(x), modify(x);
else {
read(l), read(r), ans = query(l, r), ret = 0;
ret = ans.sum[0][0] + ans.sum[0][1] + ans.sum[0][2]
+ ans.sum[2][0] + ans.sum[2][1] + ans.sum[2][2]
+ ans.sum[3][2];
printf("%lld\n", ret);
}
}
return 0;
}

Bzoj5294/洛谷P4428 [Bjoi2018]二进制(线段树)的更多相关文章

  1. BZOJ5294 BJOI2018 二进制 线段树

    传送门 因为每一位\(\mod 3\)的值为\(1,2,1,2,...\),也就相当于\(1,-1,1,-1,...\) 所以当某个区间的\(1\)的个数为偶数的时候,一定是可行的,只要把这若干个\( ...

  2. 【BZOJ】1012: [JSOI2008]最大数maxnumber /【洛谷】1198(线段树)

    Description 现在请求你维护一个数列,要求提供以下两种操作:1. 查询操作.语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值.限制:L不超过当前数列的长度.2. 插 ...

  3. 洛谷题解P4314CPU监控--线段树

    题目链接 https://www.luogu.org/problemnew/show/P4314 https://www.lydsy.com/JudgeOnline/problem.php?id=30 ...

  4. 洛谷P3372/poj3468(线段树lazy_tag)(询问区间和,支持区间修改)

    洛谷P3372 //线段树 询问区间和,支持区间修改 #include <cstdio> using namespace std; struct treetype { int l,r; l ...

  5. 洛谷P4065 [JXOI2017]颜色(线段树)

    题意 题目链接 Sol 线段树板子题都做不出来,真是越来越菜了.. 根据题目描述,一个合法区间等价于在区间内的颜色没有在区间外出现过. 所以我们可以对于每个右端点,统计最长的左端点在哪里,刚开始以为这 ...

  6. 洛谷P5111 zhtobu3232的线段树

    题意:给定线段树,上面若干个节点坏了,求能表示出多少区间. 区间能被表示出当且仅当拆出来的log个节点都是好的. 解:每个区间在最浅的节点处计算答案. 对于每个节点维护从左边过来能有多少区间,从右边过 ...

  7. 洛谷P3960 列队 NOIp2017 线段树/树状数组/splay

    正解:动态开点线段树 解题报告: 传送门! 因为最近学主席树的时候顺便get到了动态开点线段树?刚好想起来很久很久以前就想做结果一直麻油做的这题,,,所以就做下好了QAQ 然后说下,这题有很多种方法, ...

  8. 题解——洛谷P2781 传教(线段树)

    可以说是数据结构学傻了的典型案例了 昨天跳到这题上 然后思考了一下 噫!好!线段树裸题 然后打完板子,发现\(  n \le 10^9 \) 显然线段树直接做不太行 然后这题又只有普及的难度 然后我就 ...

  9. 洛谷P4198 楼房重建(线段树)

    题意 题目链接 Sol 别问我为什么发两遍 就是为了骗访问量 这个题的线段树做法,,妙的很 首先一个显然的结论:位置\(i\)能被看到当且仅当\(\frac{H_k}{k} < \frac{H_ ...

随机推荐

  1. Java实现各种内部排序算法

    数据结构中常见的内部排序算法: 插入排序:直接插入排序.折半插入排序.希尔排序 交换排序:冒泡排序.快速排序 选择排序:简单选择排序.堆排序 归并排序.基数排序.计数排序 直接插入排序: 思想:每次将 ...

  2. Scrapy爬虫框架之爬取校花网图片

    Scrapy Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架. 其可以应用在数据挖掘,信息处理或存储历史数据等一系列的程序中.其最初是为了页面抓取 (更确切来说, 网络抓取 )所设 ...

  3. php 传递赋值和地址赋值 &

    更多内容推荐微信公众号,欢迎关注: 1.传递赋值 $a = 1; $b = 2; $a = $b; echo $a,$b; //结果为:5 5 2.地址赋值 $a = 1; $b = 2; $a = ...

  4. 查看GCC的内置宏定义

    开发过程中我们常常需要使用宏定义.. 为了尽可能多的使用GCC为我们提供的特性,首先我们需要知道gcc提供了那些特性... gcc -dM -E - < /dev/null 没错,就这么一句话就 ...

  5. c++细节--section1

    1.register声明的变量为寄存器变量,因此没有地址,不能对它取地址操作. 2.[用错sizeof]当数组作为函数参数传递时,数组会退化为同类型的指针. 3.每个成员在成员初始化列表中只能出现一次 ...

  6. 让arch阻止某个软件包的升级

    我更新了eclipse-java Mars版本的,感觉特别的卡,而且还有好多bug,不知道为什么,因此我去官网下载了luna版本的eclipse的安装包,不知道怎么下载的点击这里,然后安装luna版本 ...

  7. JavaScript进阶--慕课网学习笔记

                         JAVASCRIPT—进阶篇 给变量取个名字(变量命名) 变量名字可以任意取,只不过取名字要遵循一些规则: 1.必须以字母.下划线或美元符号开头,后面可以跟字 ...

  8. STL容器基本功能与分类

    STL有7中容器. 分别为: vector 向量 <vector>(头文件) 随机访问容器.顺序容器 deque 双端队列 <deque> 随机访问容器.顺序容器 list   ...

  9. XXX变种-防火墙放行自身

    1.利用防火墙命令放行自身手法 netsh firewall add allowedprogram "C:\Users\USER\AppData\Local\Temp\Discord Can ...

  10. ADB安装

    1,下载解压 http://adbshell.com/downloads 2,配置路径 比如解压后我放在了C:\Program Files\adb 电脑-->属性-->高级系统设置--&g ...