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 ...
随机推荐
- python之路——9
王二学习python的笔记以及记录,如有雷同,那也没事,欢迎交流,wx:wyb199594 学习内容 1.文件处理 1.打开文件 open(‘路径’,‘打开方式’) 打开方式 r w a r+ w+ ...
- CVE-2017-16995 漏洞利用
Ubuntu本地提权(CVE-2017-16995)漏洞复现 在 ichunqiu 看到的环境(https://www.ichunqiu.com/course/61487),觉得有意思就记录一下. 实 ...
- ffmpeg 编译
下载FFmpeg git clone https://git.ffmpeg.org/ffmpeg.git 配置编译FFmpeg ./configure --prefix=host --enable-s ...
- eclipse中svn切换用户
如果永久保存svn用户账号及密码,再更换svn用户时需要先删除C:\Users\Administrator\AppData\Roaming下的Subversion文件,然后在刷新eclipse中的sv ...
- 学习excel的使用技巧二批量复制
1 选中要操作的部分 2 CTRL+G 打开定位 3 点击 定位条件 4 选择空值 5 输入=号 然后键盘的 方向键 向上 6 按住CTRL+回车 即可实现 批量复制
- java——IO流01
移动文件有一种简单方法,不需要复制文件再删除文件. package com.unir.test01; import java.io.File; import java.io.IOException; ...
- leetcode85
class Solution { public int maximalRectangle(char[][] matrix) { if(matrix == null || matrix.length = ...
- ElasicSearch(4) 与jest结合
https://spring.io/projects/spring-data-elasticsearch https://docs.spring.io/spring-data/elasticsearc ...
- 无法在正在进行内容生成时调用 StartAt
刚遇到一个奇怪的问题,用户点击创建销售订单的时候,弹出个 无法在正在进行内容生成时调用 StartAt,查看详细报错. ystem.InvalidOperationException: 无法在正在进行 ...
- P1147连续自然数和-(尺取法)
https://www.luogu.org/problemnew/show/P1147 题意:输入一个n,求连续几个数加起来等于n,输出这几个连续的数的第一个和最后一个.10<=n<=20 ...