2019.02.12 bzoj5294: [Bjoi2018]二进制(线段树)
传送门
题意简述:
给出一个长度为nnn的二进制串。
你需要支持如下操作:
- 修改每个位置:1变0,0变1
- 询问对于一个区间的子二进制串有多少满足重排之后转回十进制值为333的倍数(允许前导000)。
思路:
考虑一个xxx位的包含有yyy个111的二进制串,它是333的倍数当如下任意一个条件成立:
- yyy是偶数。
- yyy是大于111的奇数且x−y>=2x-y>=2x−y>=2
emmmmemmmmemmmm感觉不是很好维护。
于是我们正难则反,跑去求不合法的方案数,这个二进制串不合法如下任意一个条件成立:
- yyy是奇数大于111且x−y<2x-y<2x−y<2
- y=1y=1y=1
这个答案可以用线段树维护了(其实上面的也可以只是感觉不太好写)。
我们定义两类状态:
- C0/1,0/1,0/1,0/1C_{0/1,0/1,0/1,0/1}C0/1,0/1,0/1,0/1表示从区间左/右端点开始的连续一段二进制串,满足其中有偶/奇数个111,串中111的个数不大于/大于111个,串中有0/10/10/1个000的这样的二进制串个数。
- D0/1,0/1D_{0/1,0/1}D0/1,0/1表示从区间左/右端点开始的连续一段二进制串,满足其中有0/10/10/1个111的这样的二进制串的个数。
然后分类转移一下即可。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int N=1e5+5;
typedef long long ll;
int n;
bool a[N];
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
inline int add(const ll&a,const ll&b){return !a*b?0:a+b;}
namespace SGT{
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
struct Node{int l,r,det;ll ans,c[2][2][2][2],d[2][2];}T[N<<2];
inline Node operator+(const Node&a,const Node&b){
Node ret;
ret.l=a.l,ret.r=b.r,ret.det=a.det+b.det,ret.ans=a.ans+b.ans,memset(ret.c,0,sizeof(ret.c));
ret.d[0][0]=a.d[0][0]+(a.det?0:b.d[0][0]);
ret.d[0][1]=a.d[0][1]+(a.det<2?b.d[0][1-a.det]:0);
ret.d[1][0]=b.d[1][0]+(b.det?0:a.d[1][0]);
ret.d[1][1]=b.d[1][1]+(b.det<2?a.d[1][1-b.det]:0);
for(ri i=0;i<2;++i)for(ri j=0;j<2;++j)for(ri k=0;k<2;++k)ret.c[0][i][j][k]=a.c[0][i][j][k],ret.c[1][i][j][k]=b.c[1][i][j][k];
int tl=a.r-a.l+1-a.det,tr=b.r-b.l+1-b.det;
for(ri k=0;k+tl<2;++k){
ret.c[0][0][0][k+tl]+=a.det?0:b.c[0][0][0][k];
ret.c[0][0][1][k+tl]+=b.c[0][a.det&1][1][k]+(a.det?b.c[0][a.det&1][0][k]:0);
ret.c[0][1][0][k+tl]+=a.det<2?b.c[0][1-a.det][0][k]:0;
ret.c[0][1][1][k+tl]+=b.c[0][(a.det&1)^1][1][k]+(a.det>1?b.c[0][(a.det&1)^1][0][k]:0);
}
for(ri k=0;k+tr<2;++k){
ret.c[1][0][0][k+tr]+=b.det?0:a.c[1][0][0][k];
ret.c[1][0][1][k+tr]+=a.c[1][b.det&1][1][k]+(b.det?a.c[1][b.det&1][0][k]:0);
ret.c[1][1][0][k+tr]+=b.det<2?a.c[1][1-b.det][0][k]:0;
ret.c[1][1][1][k+tr]+=a.c[1][(b.det&1)^1][1][k]+(b.det>1?a.c[1][(b.det&1)^1][0][k]:0);
}
for(ri i=0;i<2;++i)for(ri j=0;i+j<2;++j){
ret.ans+=a.c[1][0][0][i]*b.c[0][1][1][j]+a.c[1][0][1][i]*b.c[0][1][1][j]+a.c[1][0][1][i]*b.c[0][1][0][j];
ret.ans+=a.c[1][1][0][i]*b.c[0][0][1][j]+a.c[1][1][1][i]*b.c[0][0][1][j]+a.c[1][1][1][i]*b.c[0][0][0][j];
}
ret.ans+=a.d[1][0]*b.d[0][1]+a.d[1][1]*b.d[0][0];
return ret;
}
inline void solve(int p){
T[p].ans=T[p].det=0,memset(T[p].c,0,sizeof(T[p].c)),memset(T[p].d,0,sizeof(T[p].d));
if(a[T[p].l])T[p].ans=T[p].det=T[p].c[0][1][0][0]=T[p].c[1][1][0][0]=T[p].d[0][1]=T[p].d[1][1]=1;
else T[p].c[0][0][0][1]=T[p].c[1][0][0][1]=T[p].d[0][0]=T[p].d[1][0]=1;
}
inline void build(int p,int l,int r){
T[p].l=l,T[p].r=r;
if(l==r)return solve(p);
build(lc,l,mid),build(rc,mid+1,r),T[p]=T[lc]+T[rc];
}
inline void update(int p,int k){
if(T[p].l==T[p].r)return solve(p);
update(k<=mid?lc:rc,k),T[p]=T[lc]+T[rc];
}
inline Node query(int p,int ql,int qr){
if(ql<=T[p].l&&T[p].r<=qr)return T[p];
if(qr<=mid)return query(lc,ql,qr);
if(ql>mid)return query(rc,ql,qr);
return query(lc,ql,mid)+query(rc,mid+1,qr);
}
}
int main(){
n=read();
for(ri i=1;i<=n;++i)a[i]=read();
SGT::build(1,1,n);
for(ri tt=read(),op,l,r;tt;--tt){
op=read();
if(op==1)a[l=read()]^=1,SGT::update(1,l);
else l=read(),r=read(),cout<<(ll)(r-l+1)*(r-l+2)/2-SGT::query(1,l,r).ans<<'\n';
}
return 0;
}
2019.02.12 bzoj5294: [Bjoi2018]二进制(线段树)的更多相关文章
- BZOJ5294 BJOI2018 二进制 线段树
传送门 因为每一位\(\mod 3\)的值为\(1,2,1,2,...\),也就相当于\(1,-1,1,-1,...\) 所以当某个区间的\(1\)的个数为偶数的时候,一定是可行的,只要把这若干个\( ...
- 2019.02.26 bzoj4311: 向量(线段树分治+凸包)
传送门 题意: 支持插入一个向量,删去某一个现有的向量,查询现有的所有向量与给出的一个向量的点积的最大值. 思路: 考虑线段树分治. 先对于每个向量处理出其有效时间放到线段树上面,然后考虑查询:对于两 ...
- 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 题目描述 请实现一个数据结构支持以下操作:区间循环左右移,区间与,区间或,区间求和. 输入描述: 第一行 ...
- 2019.01.19 bzoj5457: 城市(线段树合并)
传送门 线段树合并菜题. 题意简述:给一棵树,每个节点有bib_ibi个aia_iai民族的人,问对于每棵子树,子树中哪个民族的人最多,有多少人. 思路: 直接上线段树合并,边合并边维护答案即可. ...
- 【2019北京集训2】duck 线段树优化建图+tarjan
题目大意:给你$n$个点,第$i$个点有点权$v_i$.你需要将这$n$个点排成一排,第$i$个点的点权能被累加当且仅当这个点前面存在编号在$[l_i,r_i]$中的点,问你这些点应该如何排列,点权和 ...
- 2018.09.12 poj2376Cleaning Shifts(线段树+简单dp)
传送门 貌似贪心能过啊%%%. 本蒟蒻写的线段树优化dp. 式子很好推啊. f[i]表示覆盖1~i所需的最小代价. 那么显然对于一个区间[li,ri]" role="present ...
随机推荐
- 简单的知识图谱,neo4j+python
因为研究方向是知识图谱,就有兴致想要构建一个简单的知识图谱,就在网上查找了一下,参考了neo4j搭建简单的金融知识图谱的思想,就着手从零开始构建. 1.首先就要考虑数据的获得,因为之前没有接触过爬虫之 ...
- 《女神异闻录 5》的 UI 设计
转自:https://www.zhihu.com/question/50995871?sort=created <女神异闻录5>是近两年最为火热的JRPG游戏之一,它的出色不仅在于剧情暗讽 ...
- 转:细说ASP.NET Windows身份认证
转自:https://www.cnblogs.com/fish-li/archive/2012/05/07/2486840.html 细说ASP.NET Windows身份认证 阅读目录 开始 认识A ...
- Vue+webpack项目配置便于维护的目录结构
新建项目的时候创建合理的目录结构便于后期的维护是很重要 环境:vue.webpack 目录结构: 项目子目录结构 子目录结构都差不多,主要目录是在src下面操作 src目录结构 src/common ...
- python3:实现字符串的全排列(有重复字符)
抛出问题 求任意一个字符串的全排列组合,例如a='123',输出 123,132,213,231,312,321. 解决方案 #字符串任意两个位置字符交换 def str_replace(str, x ...
- springboot学习目录
1.spring boot 简单示例 一个简单的springboot 例子 https://www.cnblogs.com/shoshana-kong/p/9641696.html 2. sprin ...
- 让UITableView的section header view不悬停的方法
当 UITableView 的 style 属性设置为 Plain 时,这个tableview的section header在滚动时会默认悬停在界面顶端.取消这一特性的方法有两种: 将 style 设 ...
- VS2015+Opencv3.2配置(一次配好)
对于 VS2015+QT5.8的配置我就不介绍了,由于我配置的比较早,具体有的东西忘掉了,大家可以参考下面这几篇文章. 留白留白留白留白留白(稍后补) 对于Opencv+VS的配置是我重点要说的内容. ...
- Android开发中常见的设计模式(四)——策略模式
策略模式定义了一些列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变换. 假设我们要出去旅游,而去旅游出行的方式有很多,有步行,有坐火车,有坐飞机等等 ...
- win10 KMS命令激活步骤<转>
三.win10 KMS命令激活步骤如下: 1.右键点击开始图标,弹出这个菜单,选择[windows powershell(管理员)],或者命令提示符管理员: 2.打开命令窗口,复制这个命令slmgr ...