正题

luogu 4428


题目大意

给你一个01串,让你进行一下两种操作:
1.将其中一位取反
2.问你某一段中有多少个子串满足有一种排列方案,使得组成的二进制数是3的倍数


解题思路

不难发现,因为2%3=2,所以2的幂%3的结果按121212…的规律循环

如果一个子串中1的个数为偶数个,可以让它们在相邻位,这样就可以被三整除

如果一个子串中1的个数为奇数个,那么要先拿出三个1,分开放,组成10101,这样才能让其被3整除

\\

那么就有了以下两种情况:

1.1的个数为偶数
2.1的个数为奇数,且num1>1,num0⩾2num_1>1,num_0\geqslant 2num1​>1,num0​⩾2

如果计算这两种情况,会十分困难

那么考虑用合法方案数=总方案数-不合法方案数

不合法的用以下情况

1.1的个数为奇数,且0的个数小于2
2.只有1个1

\\
以上两种情况可以用线段树计算

每个位置维护以下信息:

1.ld/rd0/1,0/1ld/rd_{0/1,0/1}ld/rd0/1,0/1​强行经过左/右端点且0的出现次数为0/1,1的出现次数为偶数/奇数
2.lo/ro0/1/2lo/ro_{0/1/2}lo/ro0/1/2​,强行经过左/右端点且恰好出现1次,0的出现次数为0/1/大于2
3.s0,s1,l0,r0,0的个数,1的个数,以左端点为起点0的个数,以右端点为终点0的个数

然后计算不合法的,把01和10这两种不合法的情况减掉


代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ls x*2
#define rs x*2+1
#define N 100100
#define ll long long
using namespace std;
ll n, m, x, y, a[N];
struct node
{ll ld[2][2], rd[2][2], lo[3], ro[3], s0, l0, r0, s1, s;void clean(){memset(ld, 0, sizeof(ld));memset(rd, 0, sizeof(rd));memset(lo, 0, sizeof(lo));memset(ro, 0, sizeof(ro));s1 = s0 = l0 = r0 = s = 0;}void add(ll x){clean();if (x) ld[0][1] = rd[0][1] = lo[0] = ro[0] = s1 = s = 1;else ld[1][0] = rd[1][0] = s0 = l0 = r0 = 1;return;}
}T[N<<2];
node merge(node a, node b)
{node c;c.clean();for (ll i = 0; i <= 1; ++i)for (ll j = 0; j <= 1; ++j){c.ld[i][j] += a.ld[i][j];//计算ld/rdc.rd[i][j] += b.rd[i][j];if (i >= a.s0) c.ld[i][j] += b.ld[i - a.s0][j^(a.s1&1)];//左边的0不够,右边的也要算进去if (i >= b.s0) c.rd[i][j] += a.rd[i - b.s0][j^(b.s1&1)];}for (ll i = 0; i <= 2; ++i){c.lo[i] += a.lo[i];//计算lo/roc.ro[i] += b.ro[i];if (!a.s1) c.lo[min(2ll, i + a.s0)] += b.lo[i];//左边没有1,右边的也要算进去if (!b.s1) c.ro[min(2ll, i + b.s0)] += a.ro[i];}if (a.s1 == 1 && b.l0)//左边只有1个1,右边的0也要算进去{if (!a.s0) c.lo[1]++, c.lo[2] += b.l0 - 1;//10的情况只有1个0,其他都有2个以上的0else c.lo[2] += b.l0;}if (b.s1 == 1 && a.r0){if (!b.s0) c.ro[1]++, c.ro[2] += a.r0 - 1;else c.ro[2] += a.r0;}c.s0 = a.s0 + b.s0;c.s1 = a.s1 + b.s1;c.l0 = a.s1 ? a.l0 : a.l0 + b.l0;//左边全是0,右边的也要算进去c.r0 = b.s1 ? b.r0 : b.r0 + a.r0;c.s = a.s + b.s;//子区间的c.s += a.rd[0][0] * (b.ld[0][1] + b.ld[1][1]);//奇数个1,且0的个数小于2c.s += a.rd[0][1] * (b.ld[0][0] + b.ld[1][0]);c.s += a.rd[1][0] * b.ld[0][1];c.s += a.rd[1][1] * b.ld[0][0];if (a.r0) c.s += a.r0 * (b.lo[0] + b.lo[1] + b.lo[2]) - b.lo[0];//只有1个1的,要减去0+1的情况,因为0的个数小于2,前面有计算过if (b.l0) c.s += b.l0 * (a.ro[0] + a.ro[1] + a.ro[2]) - a.ro[0];return c;
}
void build(ll x, ll l, ll r)//线段树
{if (l == r){T[x].add(a[l]);return;}ll mid = l + r >> 1;build(ls, l, mid);build(rs, mid + 1, r);T[x] = merge(T[ls], T[rs]);return;
}
void change(ll x, ll l, ll r, ll y, ll z)
{if (l == r){T[x].add(z);return;}ll mid = l + r >> 1;if (y <= mid) change(ls, l, mid, y, z);else change(rs, mid + 1, r, y, z);T[x] = merge(T[ls], T[rs]);return;
}
node ask(ll x, ll L, ll R, ll l, ll r)
{if (L == l && R == r) return T[x];ll mid = L + R >> 1;if (r <= mid) return ask(ls, L, mid, l, r);else if (l > mid) return ask(rs, mid + 1, R, l, r);else return merge(ask(ls, L, mid, l, mid), ask(rs, mid + 1, R, mid + 1, r));
}
int main()
{scanf("%lld", &n);for (ll i = 1; i <= n; ++i)scanf("%lld", &a[i]);build(1, 1, n);scanf("%lld", &m);while(m--){scanf("%lld", &x);if (x == 1){scanf("%lld", &x);a[x] ^= 1;change(1, 1, n, x, a[x]);}else{scanf("%lld%lld", &x, &y);printf("%lld\n", (y - x + 1) * (y - x + 2) / 2 - ask(1, 1, n, x, y).s);//总方案数减去不合法方案数}}return 0;
}

【线段树】二进制(luogu 4428)相关推荐

  1. 【线段树】[LUOGU 守墓人] [LUOGU 维护序列] 线段树模板题

    题目: 题目链接:[LUOGU 守墓人] 题解: 线段树单点修改,区间修改,单点查询,区间查询,一系列线段树基本操作,模板打就好. (回头再补一个分块和树状数组的这种板子题,就是用分块和树状数组再写一 ...

  2. 皮卡丘的梦想2(线段树+二进制状态压缩)

    Description 一天,一只住在 501 实验室的皮卡丘决定发奋学习,成为像 LeiQ 一样的巨巨,于是他向镇上的贤者金桔请教如何才能进化成一只雷丘. 金桔告诉他需要进化石才能进化,并给了他一个 ...

  3. New Year Tree(dfs序+线段树+二进制)

    题意: 给出一棵 n个节点的树,根节点为 1.每个节点上有一种颜色 ci.m次操作.操作有两种: 1 u c:将以 u为根的子树上的所有节点的颜色改为c. 2 u:询问以 u为根的子树上的所有节点的颜 ...

  4. luogu P5142 区间方差(线段树、乘法逆元)

    luogu P5142 区间方差 本题要求维护模区间方差,很明显是一道数据结构题. 我们化简方差公式: 而平均数等于 可以发现,我们只需要维护序列的区间和和区间平方和,就可以维护平均数和方差. 区间和 ...

  5. luogu P4085 [USACO17DEC]Haybale Feast(尺取法 + 线段树)

    C.luogu P4085 [USACO17DEC]Haybale Feast 题目链接 直接开一个线段树维护即可. 注意开ll 线段树维护最大值.注意下标别写错了,比如tr[r] #include& ...

  6. BZOJ 1920 Luogu P4217 [CTSC2010]产品销售 (模拟费用流、线段树)

    题目链接 (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=1920 (luogu) https://www.luogu.org/prob ...

  7. UOJ #395 BZOJ 5417 Luogu P4770 [NOI2018]你的名字 (后缀自动机、线段树合并)

    NOI2019考前做NOI2018题.. 题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=5417 (luogu) http ...

  8. Luogu P1198 BZOJ 1012 最大数 (线段树)

    Luogu P1198 BZOJ 1012 最大数 (线段树) 手动博客搬家: 本文发表于20170821 14:32:05, 原地址https://blog.csdn.net/suncongbo/a ...

  9. 【BZOJ3821/UOJ46】玄学(二进制分组,线段树)

    [BZOJ3821/UOJ46]玄学(二进制分组,线段树) 题面 BZOJ UOJ 题解 呜,很好的题目啊QwQ. 离线做法大概可以线段树分治,或者直接点记录左右两次操作时的结果,两个除一下就可以直接 ...

最新文章

  1. 一起学设计模式 - 观察者模式
  2. 鸟哥的私房菜 第0章
  3. 【题解】 hdu2955 Robberies
  4. system verilog随机函数_systemverilog中的随机化激励.pdf
  5. 检测文件是否有BOM 头,并删除BOM头(php)
  6. Android商城开发系列(十)—— 首页活动广告布局实现
  7. javaweb中运用fileupload上传文件
  8. WIN7 X64完美卸载SQL2008 R2并重装SQL2008
  9. 有关各浏览器内核的区别
  10. 手机12306买卧铺下铺技巧_手机12306怎么买下铺(微信买火车票指定下铺)
  11. vue中局部过滤器和全局过滤器的使用
  12. 最全的英语收藏夹(精品)
  13. 红米note3android驱动,红米Note3手机驱动
  14. 下单后,订单拆单能怎拆?
  15. linux 7分区 fdisk,CentOS7创建逻辑卷,fdisk分区方式。
  16. Tp5生成带头像二维码海报(带文字描述,居中调整)
  17. 微信小程序实现共享数据
  18. 智能家居(12)——树莓派USB摄像头捕捉人脸并识别
  19. Android 安卓动画 属性动画 - 移动动画
  20. 腾讯云轻量级服务器怎么搭建网站,腾讯云轻量应用服务器新手教程:快速搭建网站...

热门文章

  1. 树莓派安装python3.5_梦见树_周公解梦梦到树是什么意思_做梦梦见树好不好_周公解梦官网...
  2. c语言 extern_Visual C++ 6.0环境中C语言extern 变量使用过程报错
  3. 计算机所有数据的表示方式都是用,计算机数据表示
  4. C++ 学习之旅(4)——调试Debug
  5. 一文梳理JavaScript中常见的七大继承方案
  6. 平均成绩计算机控件,计算机技术基础(第十二章 文件 )
  7. [剑指offer]面试题31:连续子数组的最大和
  8. http.ListenAndServe()到底做了什么?
  9. python数组赋值给变量_Python:将数组中的元素导出到变量中 (unpacking)
  10. 分区式存储管理c++_分区机要变形缝,纵横交接卫浴厨:防火阀参数的高效记忆口诀...