【BZOJ5294】[BJOI2018]二进制(线段树)
【BZOJ5294】[BJOI2018]二进制(线段树)
题面
题解
二进制串在模\(3\)意义下,每一位代表的余数显然是\(121212\)这样子交替出现的。
其实换种方法看,就是\(1,-1,1,-1,...\)
如果询问一个二进制串能否被\(3\)整除,那么只需要考虑奇数位上的\(1\)的个数和偶数位上的\(1\)的个数就行了。
如果可以重排,我们来考虑如何分配。
首先对于一个长度为\(len\)的区间,模\(3\)余\(1\)的位有\([\frac{len+1}{2}]\)个,余\(-1\)的有\([\frac{len}{2}]\)个。假设要分配\(k\)个\(1\)。
凑成\(3\)的倍数的情况一定是\(1,-1\)两两配对,剩下较多的那个的数量是\(3\)的倍数。
如果\(k\)是偶数那么一定可以两两配对。
如果\(k\)是奇数的话,就只能\(k-3\)个\(1\)均匀分配给\(-1,1\),剩下\(3\)个分配给\(1\)。
那么需要满足\(\frac{k+3}{2}\le [\frac{len+1}{2}]\),拆开后如果\(len\)是奇数则要满足\(k\le len-2\),如果\(len\)是偶数则满足\(k\le len-3\)。
那么这个条件再进一步就是,如果\(0\)的个数\(\ge 3\),那么一定满足。
如果\(0\)的个数为\(2\),此时\(len=k+2\) 为奇数,也满足。
所以不合法的情况就是
- 只有一个\(1\)的区间(\(k\lt 3\),且\(k\)为奇数就只有\(1\))
- 出现了奇数个\(1\),且\(0\)的个数为\(0/1\)。
因为要做到不重,所以第一个条件可以补充成“区间内只有\(1\)个\(1\),且\(0\)的个数不少于\(2\)个”。
答案就可以用总的连续子序列的个数减去不合法的数量。
可以用线段树维护不合法的连续子序列的数量。
考虑合并两个节点之后如何产生贡献,
设\(dl[0/1][0/1]\)表示强制选择左端点的一段连续区间中,\(0\)的出现次数为\(0/1\),\(1\)的出现次数的奇偶性为\(0/1\)的序列个数,\(dr\)同理。
\(fl[0/1/2]\)表示强制经过左端点,\(1\)恰好出现了\(1\)次,且\(0\)的出现次数为\(0,1,\ge 2\)的序列个数。\(fr\)同理。
再统计一下左右连续的\(0\)的个数,以及区间内\(0/1\)的个数。
每次考虑跨过两段的不合法区间,统计答案即可。
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 100100
#define lson (now<<1)
#define rson (now<<1|1)
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,Q,a[MAX];
struct data
{
ll dl[2][2],dr[2][2],fl[3],fr[3],l0,r0,s;
int s0,s1;
void init()
{
dl[0][0]=dr[0][0]=dl[0][1]=dr[0][1]=dl[1][0]=dr[1][0]=dl[1][1]=dr[1][1]=0;
fl[0]=fr[0]=fl[1]=fr[1]=fl[2]=fr[2]=0;
l0=r0=s0=s1=s=0;
}
data(){init();}
void pre(int x)
{
init();
if(x)dl[0][1]=dr[0][1]=s1=s=fl[0]=fr[0]=1;
else dl[1][0]=dr[1][0]=s0=l0=r0=1;
}
}t[MAX<<2];
data Merge(data A,data B)
{
data c;c.init();
for(int i=0;i<2;++i)
for(int j=0;j<2;++j)
{
c.dl[i][j]+=A.dl[i][j];
c.dr[i][j]+=B.dr[i][j];
if(i>=A.s0)c.dl[i][j]+=B.dl[i-A.s0][j^(A.s1&1)];
if(i>=B.s0)c.dr[i][j]+=A.dr[i-B.s0][j^(B.s1&1)];
}
for(int i=0;i<3;++i)
{
c.fl[i]+=A.fl[i];c.fr[i]+=B.fr[i];
if(!A.s1)c.fl[min(2,i+A.s0)]+=B.fl[i];
if(!B.s1)c.fr[min(2,i+B.s0)]+=A.fr[i];
}
if(A.s1==1&&B.l0)c.fl[min(2ll,A.s0+B.l0)]+=1,c.fl[2]+=B.l0-1;
if(B.s1==1&&A.r0)c.fr[min(2ll,B.s0+A.r0)]+=1,c.fr[2]+=A.r0-1;
c.l0=(A.s1==0)?A.l0+B.l0:A.l0;
c.r0=(B.s1==0)?B.r0+A.r0:B.r0;
c.s0=A.s0+B.s0;c.s1=A.s1+B.s1;
c.s=A.s+B.s;
c.s+=A.dr[0][0]*(B.dl[0][1]+B.dl[1][1]);
c.s+=A.dr[0][1]*(B.dl[0][0]+B.dl[1][0]);
c.s+=A.dr[1][0]*B.dl[0][1];
c.s+=A.dr[1][1]*B.dl[0][0];
if(B.l0)c.s+=B.l0*(A.fr[0]+A.fr[1]+A.fr[2])-A.fr[0];
if(A.r0)c.s+=A.r0*(B.fl[0]+B.fl[1]+B.fl[2])-B.fl[0];
return c;
}
void Build(int now,int l,int r)
{
if(l==r){t[now].pre(a[l]);return;}
int mid=(l+r)>>1;
Build(lson,l,mid);Build(rson,mid+1,r);
t[now]=Merge(t[lson],t[rson]);
}
void Modify(int now,int l,int r,int p)
{
if(l==r){t[now].pre(a[l]);return;}
int mid=(l+r)>>1;
if(p<=mid)Modify(lson,l,mid,p);
else Modify(rson,mid+1,r,p);
t[now]=Merge(t[lson],t[rson]);
}
data Query(int now,int l,int r,int L,int R)
{
if(L==l&&R==r)return t[now];
int mid=(l+r)>>1;
if(R<=mid)return Query(lson,l,mid,L,R);
if(L>mid)return Query(rson,mid+1,r,L,R);
return Merge(Query(lson,l,mid,L,mid),Query(rson,mid+1,r,mid+1,R));
}
int main()
{
n=read();
for(int i=1;i<=n;++i)a[i]=read();
Build(1,1,n);
Q=read();
while(Q--)
{
int opt=read(),l=read(),r;
if(opt==1)a[l]^=1,Modify(1,1,n,l);
else r=read(),printf("%lld\n",1ll*(r-l+1)*(r-l+2)/2-Query(1,1,n,l,r).s);
}
return 0;
}
【BZOJ5294】[BJOI2018]二进制(线段树)的更多相关文章
- BZOJ5294 BJOI2018 二进制 线段树
传送门 因为每一位\(\mod 3\)的值为\(1,2,1,2,...\),也就相当于\(1,-1,1,-1,...\) 所以当某个区间的\(1\)的个数为偶数的时候,一定是可行的,只要把这若干个\( ...
- 2019.02.12 bzoj5294: [Bjoi2018]二进制(线段树)
传送门 题意简述: 给出一个长度为nnn的二进制串. 你需要支持如下操作: 修改每个位置:1变0,0变1 询问对于一个区间的子二进制串有多少满足重排之后转回十进制值为333的倍数(允许前导000). ...
- BZOJ5294 BJOI2018二进制(线段树)
二进制数能被3整除相当于奇数.偶数位上1的个数模3同余.那么如果有偶数个1,一定存在重排方案使其合法:否则则要求至少有两个0且至少有3个1,这样可以给奇数位单独安排3个1. 考虑线段树维护区间内的一堆 ...
- BZOJ5294 [BJOI2018] 二进制 【线段树】
BJOI的题目感觉有点难写 题目分析: 首先推一波结论.接下来的一切都在模3意义下 现在我们将二进制位重组,不难发现的是2^0≡1,2^1≡2,2^2≡1,2^3≡2....所以我们考虑这样的式子 2 ...
- 中国石油大学(华东)暑期集训--二进制(BZOJ5294)【线段树】
问题 C: 二进制 时间限制: 1 Sec 内存限制: 128 MB提交: 8 解决: 2[提交] [状态] [讨论版] [命题人:] 题目描述 pupil发现对于一个十进制数,无论怎么将其的数字 ...
- nowcoder 211E - 位运算?位运算! - [二进制线段树][与或线段树]
题目链接:https://www.nowcoder.com/acm/contest/211/E 题目描述 请实现一个数据结构支持以下操作:区间循环左右移,区间与,区间或,区间求和. 输入描述: 第一行 ...
- Bzoj5294/洛谷P4428 [Bjoi2018]二进制(线段树)
题面 Bzoj 洛谷 题解 考虑一个什么样的区间满足重组之后可以变成\(3\)的倍数.不妨设\(tot\)为一个区间内\(1\)的个数.如果\(tot\)是个偶数,则这个区间一定是\(3\)的倍数,接 ...
- 洛谷P4428二进制 [BJOI2018] 线段树
正解:线段树 解题报告: 传送门! 话说开始看到这题的时候我想得hin简单 因为关于%3有个性质就是说一个数的各个位数之和%3=这个数%3嘛,小学基础知识? 我就想着,就直接建一棵树,只是这棵树要用个 ...
- POJ 2777 Count Color(线段树染色,二进制优化)
Count Color Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 42940 Accepted: 13011 Des ...
随机推荐
- RabbitMQ消息的交换
消息的交换 目录 RabbitMQ-从基础到实战(1)— Hello RabbitMQ RabbitMQ-从基础到实战(2)— 防止消息丢失 1.简介 在前面的例子中,每个消息都只对应一个消费者,即使 ...
- K8S、云计算、大数据、编程语言
云计算.大数据.编程语言学习指南下载,100+技术课程免费学!这份诚意满满的新年技术大礼包,你Get了吗?-云栖社区-阿里云https://yq.aliyun.com/articles/691028 ...
- SQLServer数据库分页
以 项目表 PM_Project 为例. PM_Project 全部内容如下(共6条数据): 一.Top – Not In - Top 方式分页 直接的,原始的,不采用函数,纯手动挡. 分步探索过 ...
- fork分支与源分支同步代码
最进软件工程课程要团队开发做个网站项目,于是我在团队里推了使用github这种网站来协同开发,但是出现了个问题:fork后的代码无法 与源分支代码同步,导致fork分支的代码只有自己写的那部分,而不是 ...
- 6 基于Servlet 的分页
实现上图的分页功能,每页显示3条数据,每页显示3页供用户访问,点击左右双箭头时,可以跳转至上一个或者下一个大页,如点击右双箭头显示如下: 1 连接数据库的工具类 package com.latiny. ...
- [转帖]NUMA架构的CPU -- 你真的用好了么?
NUMA架构的CPU -- 你真的用好了么? 本文从NUMA的介绍引出常见的NUMA使用中的陷阱,继而讨论对于NUMA系统的优化方法和一些值得关注的方向. 文章欢迎转载,但转载时请保留本段文字,并置于 ...
- mybatis一级缓存详解
mybatis缓存分为一级缓存,二级缓存和自定义缓存.本文重点讲解一级缓存 一:前言 在介绍缓存之前,先了解下mybatis的几个核心概念: * SqlSession:代表和数据库的一次会话,向用户提 ...
- 动态SQL3
Oracle的批量操作 Oracle不支持VALUES(),(),()这种方式,所以不能用上一节所讲的方法. 有时候业务会包含很多次数据库操作,为了减少数据库连接,我们会选择一次提交大量sql, 这时 ...
- 剑指offer(8)
题目: 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 思路: 第一反应想到的是把数右移,每一位与1相与,然后判断个数,但是若输入的为负数,会出现死循环现象. 所以我们设置一个标志量 ...
- YAML配置:mapping values are not allowed here
在配置Eureka服务器配置文件的时候,出现了mapping values not allowed here的错误,原因是的冒号 ”:“后面没有空格. 原因分析:yml文件中,键值对是以": ...