正题

题目链接:https://www.luogu.com.cn/problem/P4428


题目大意

长度为\(n\)的\(0/1\)串要求支持

  1. 修改一个位置
  2. 求区间\([l,r]\)有多少个子区间重排后的二进制数可以被三整除

\(1\leq n\leq 10^5\)


解题思路

首先有\(2^{2k}\%3=1(k\in Z)\)和\(2^{2k+1}\%3=2(k\in Z)\)。

分三种情况考虑

  • 有\(1\)个\(1\)那么显然无论如何都不可以被三整除
  • 有\(2k\)个\(1\)那么我们之间都排在最后面就好了。
  • 有\(2k+1\)个\(1\)(\(k\)不能为\(0\)),那么有一种方案就是把某个在奇数位置的\(1\)放到偶数位置就可以了,此时需要区间的长度至少为\(2k+3\)。

然后具体分析一下相当于一个区间\(1\)的个数不能为\(1\)且如果是奇数个那么必须至少有两个\(0\)。

看起来很复杂可以反过来做分成以下情况

  1. 区间全是\(1\)且长度为奇数
  2. 区间有一个\(0\)且长度为偶数
  3. 区间只有一个\(1\)
  4. 由于\(2\)和\(3\)会重复一种只有一个\(1\)和一个\(0\)的情况所以需要加回这个方案

第四种是最好维护的,顺便用树状数组记录就好了

然后前三种我们对于\(0/1\)的位置分别开一个\(set\)来查询某个位置前驱/后继的0/1。

然后第三种情况我们对于每个\(1\)考虑左右的\(0\)区间然后记录在树状数组\(1\)的位置

对于第二种情况我们考虑对于每个\(0\)考虑左右的\(1\)然后记录在那个\(0\)的位置

对于第一种情况我们之间记录到区间最左端的\(0\)处。

然后统计答案的时候要记得把边界的情况考虑

写起来有点麻烦

时间复杂度\(O(n\log n)\)


code

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>
  4. #include<set>
  5. #define lowbit(x) (x&-x)
  6. #define ll long long
  7. using namespace std;
  8. const ll N=1e5+10;
  9. ll n,m,a[N],t[N],p[N];
  10. set<ll> s[2];
  11. void Change(ll x,ll val){
  12. while(x<=n){
  13. t[x]+=val;
  14. x+=lowbit(x);
  15. }
  16. return;
  17. }
  18. ll Ask(ll x){
  19. ll ans=0;
  20. while(x){
  21. ans+=t[x];
  22. x-=lowbit(x);
  23. }
  24. return ans;
  25. }
  26. ll Left(ll op,ll x)
  27. {return (*--s[op].upper_bound(x));}
  28. ll Right(ll op,ll x)
  29. {return (*s[op].lower_bound(x));}
  30. ll Count(ll n)
  31. {return (n+1)/2*(n+2-(n&1))/2;}
  32. ll Caunt(ll n)
  33. {return n*(n+1)/2;}
  34. ll Calc(ll L,ll R)
  35. {return (L/2+1)*((R+1)/2)+((L+1)/2)*(R/2+1);}
  36. void Updata(ll x){
  37. if(x<1||x>n)return;
  38. if(p[x])Change(x,-p[x]);
  39. if(a[x]){
  40. ll L=(x-Left(1,x-1)-1),R=(Right(1,x+1)-x-1);
  41. p[x]=(L+1)*(R+1)-1;
  42. }
  43. else{
  44. ll L=(x-Left(0,x-1)-1),R=(Right(0,x+1)-x-1);
  45. p[x]=Calc(L,R)+Count(R);
  46. }
  47. if(x<n&&a[x]!=a[x+1])p[x]--;
  48. Change(x,p[x]);
  49. return;
  50. }
  51. ll Get(ll x,ll l,ll r){
  52. ll L=max(Left(0,x-1),l-1),R=min(Right(0,x+1),r+1);
  53. L=x-L-1;R=R-x-1;
  54. return Calc(L,R);
  55. }
  56. ll Qet(ll x,ll l,ll r){
  57. ll L=max(Left(1,x-1),l-1),R=min(Right(1,x+1),r+1);
  58. L=x-L-1;R=R-x-1;
  59. return (L+1)*(R+1)-1;
  60. }
  61. signed main()
  62. {
  63. scanf("%lld",&n);
  64. s[0].insert(0);s[0].insert(n+1);
  65. s[1].insert(0);s[1].insert(n+1);
  66. for(ll i=1;i<=n;i++)
  67. scanf("%lld",&a[i]),s[a[i]].insert(i);
  68. for(ll i=1;i<=n;i++)
  69. Updata(i);
  70. scanf("%lld",&m);
  71. while(m--){
  72. ll op,l,r,x;
  73. scanf("%lld",&op);
  74. if(op==1){
  75. scanf("%lld",&x);
  76. s[a[x]].erase(x);
  77. a[x]=!a[x];
  78. s[a[x]].insert(x);
  79. Updata(x);
  80. Updata(Left(0,x-1));
  81. Updata(Left(1,x-1));
  82. Updata(Right(0,x+1));
  83. Updata(Right(1,x+1));
  84. }
  85. else{
  86. scanf("%lld%lld",&l,&r);
  87. ll ans=(r-l+1)*(r-l+2)/2;
  88. if(Left(1,r)<l){printf("%lld\n",ans);continue;}
  89. if(Left(0,r)<l){ans-=Count(r-l+1);printf("%lld\n",ans);continue;}
  90. ans-=Ask(r)-Ask(l-1);
  91. if(r<n&&a[r]!=a[r+1])ans--;
  92. ll Ll=Left(0,l-1),Rr=Right(0,r+1),Lr=Left(0,r),Rl=Right(0,l);
  93. ans=ans+Get(Rl,1,n)-Get(Rl,l,r);
  94. if(Lr!=Rl)ans=ans+Get(Lr,1,n)-Get(Lr,l,r);
  95. if(a[r+1])ans=ans+Count(Rr-Lr-1)-Count(r-Lr);
  96. if(a[l])ans=ans-Count(Rl-l);
  97. Ll=Left(1,l),Rr=Right(1,r),Lr=Left(1,r),Rl=Right(1,l);
  98. ans=ans+Qet(Rl,1,n)-Qet(Rl,l,r);
  99. if(Lr!=Rl)ans=ans+Qet(Lr,1,n)-Qet(Lr,l,r);
  100. // if(!a[r])ans=ans+Caunt(Rr-Rl-1)-Caunt(r-Rl);
  101. // if(!a[l])ans=ans-Caunt(Lr-l);
  102. printf("%lld\n",ans);
  103. }
  104. }
  105. return 0;
  106. }

P4428-[BJOI2018]二进制【树状数组,set】的更多相关文章

  1. BZOJ4888 [Tjoi2017]异或和 FFT或树状数组+二进制拆位

    题面 戳这里 简要题解 做法一 因为所有数的和才100w,所以我们可以直接求出所有区间和. 直接把前缀和存到一个权值数组,再倒着存一遍,大力卷积一波. 这样做在bzoj目前还过不了,但是luogu开O ...

  2. BIT 树状数组 详解 及 例题

    (一)树状数组的概念 如果给定一个数组,要你求里面所有数的和,一般都会想到累加.但是当那个数组很大的时候,累加就显得太耗时了,时间复杂度为O(n),并且采用累加的方法还有一个局限,那就是,当修改掉数组 ...

  3. POJ2828 Buy Tickets[树状数组第k小值 倒序]

    Buy Tickets Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 19012   Accepted: 9442 Desc ...

  4. UVA11525 Permutation[康托展开 树状数组求第k小值]

    UVA - 11525 Permutation 题意:输出1~n的所有排列,字典序大小第∑k1Si∗(K−i)!个 学了好多知识 1.康托展开 X=a[n]*(n-1)!+a[n-1]*(n-2)!+ ...

  5. 树状数组求第k小的元素

    int find_kth(int k) { int ans = 0,cnt = 0; for (int i = 20;i >= 0;i--) //这里的20适当的取值,与MAX_VAL有关,一般 ...

  6. 51nod1019逆序数(归并排序/树状数组)

    题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1019 题意:中文题诶- 思路: 方法1:归并排序- 归并排序过 ...

  7. POJ 3067 Japan(经典树状数组)

    基础一维树状数组  题意:左边一排 1-n 的城市,右边一排 1-m 的城市,都从上到下依次对应.接着给你一些城市对,表示城市这两个城市相连,最后问你一共有多少个交叉,其中处于城市处的交叉不算并且每个 ...

  8. 【转载】区间信息的维护与查询(一)——二叉索引树(Fenwick树、树状数组)

    在网上找到一篇非常不错的树状数组的博客,拿来转载,原文地址. 树状数组 最新看了一下区间的查询与修改的知识,最主要看到的是树状数组(BIT),以前感觉好高大上的东西,其实也不过就这么简单而已. 我们有 ...

  9. (新人的第一篇博客)树状数组中lowbit(i)=i&(-i) 的简单文字证明

    第一次写博好激动o(≧v≦)o~~初一狗语无伦次还请多多指教   先了解树状数组http://blog.csdn.net/int64ago/article/details/7429868感觉这个前辈写 ...

随机推荐

  1. PHP随手记2--获取随机n位不重复字符

    定义一个函数返回26英文字母中n位不重复随机字符 基本思路是利用内置函数生成随机数,取出该位置字母之后将其删除,再进行下一次随机,最后实现字符串拼接就ok! 代码很简单,通俗易懂,直接上代码吧: 1 ...

  2. 【spring 注解驱动开发】spring ioc 原理

    尚学堂spring 注解驱动开发学习笔记之 - Spring容器创建 Spring容器创建 1.Spring容器创建-BeanFactory预准备 2.Spring容器创建-执行BeanFactory ...

  3. spring框架学习日志一

    一.简介 1.对spring框架的简单理解 可以理解为它是一个管理对象的创建.依赖.销毁的容器 Spring 是一个开源框架. Spring 为简化企业级应用开发而生. 使用 Spring 可以使简单 ...

  4. openCV入门系列教学(一) 图像的读取、展示与保存

    序言 笔者最近做了两个CV领域的项目,因为数据量不足所以主要使用的是传统的CV方法.这时候不得不夸一句opencv库,让复杂的算法原理变得如此简单(调包调参侠表示很骄傲).所以闲暇下来对opencv的 ...

  5. xv6学习笔记(5) : 锁与管道与多cpu

    xv6学习笔记(5) : 锁与管道与多cpu 1. xv6锁结构 1. xv6操作系统要求在内核临界区操作时中断必须关闭. 如果此时中断开启,那么可能会出现以下死锁情况: 进程A在内核态运行并拿下了p ...

  6. vue中的v-cloak指令

    v-cloak不需要表达式,它会在vue实例结束编译时从绑定的html元素上移除,经常和display:none;配合使用: <div id="app" v-cloak> ...

  7. 眼见为实,看看MySQL中的隐藏列!

    在介绍mysql的多版本并发控制mvcc的过程中,我们提到过mysql中存在一些隐藏列,例如行标识.事务ID.回滚指针等,不知道大家是否和我一样好奇过,要怎样才能实际地看到这些隐藏列的值呢? 本文我们 ...

  8. kubebuilder实战之七:webhook

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  9. mysql绕过root密码登录

    绕过密码登录步骤: 一.Mysql8.0之前: 关闭服务 执行参数:mysqld --skip-grant-tables 新开窗口执行mysql,即可进入mysql 二.Mysql8.0之前: 关闭服 ...

  10. Mac超好用的软件合集和系统设置

    软件篇 这些软件好像只有动态壁纸是收费的. 推荐的都是特别小巧,更加专注特定功能,没那么多花里胡哨.当然你们有什么更好用的也可以推荐. 简单,好用才是我最喜欢的. Bob Github开源,Bob 是 ...