P4428-[BJOI2018]二进制【树状数组,set】
正题
题目链接:https://www.luogu.com.cn/problem/P4428
题目大意
长度为\(n\)的\(0/1\)串要求支持
- 修改一个位置
- 求区间\([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\)且长度为奇数
- 区间有一个\(0\)且长度为偶数
- 区间只有一个\(1\)
- 由于\(2\)和\(3\)会重复一种只有一个\(1\)和一个\(0\)的情况所以需要加回这个方案
第四种是最好维护的,顺便用树状数组记录就好了
然后前三种我们对于\(0/1\)的位置分别开一个\(set\)来查询某个位置前驱/后继的0/1。
然后第三种情况我们对于每个\(1\)考虑左右的\(0\)区间然后记录在树状数组\(1\)的位置
对于第二种情况我们考虑对于每个\(0\)考虑左右的\(1\)然后记录在那个\(0\)的位置
对于第一种情况我们之间记录到区间最左端的\(0\)处。
然后统计答案的时候要记得把边界的情况考虑
写起来有点麻烦
时间复杂度\(O(n\log n)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#define lowbit(x) (x&-x)
#define ll long long
using namespace std;
const ll N=1e5+10;
ll n,m,a[N],t[N],p[N];
set<ll> s[2];
void Change(ll x,ll val){
while(x<=n){
t[x]+=val;
x+=lowbit(x);
}
return;
}
ll Ask(ll x){
ll ans=0;
while(x){
ans+=t[x];
x-=lowbit(x);
}
return ans;
}
ll Left(ll op,ll x)
{return (*--s[op].upper_bound(x));}
ll Right(ll op,ll x)
{return (*s[op].lower_bound(x));}
ll Count(ll n)
{return (n+1)/2*(n+2-(n&1))/2;}
ll Caunt(ll n)
{return n*(n+1)/2;}
ll Calc(ll L,ll R)
{return (L/2+1)*((R+1)/2)+((L+1)/2)*(R/2+1);}
void Updata(ll x){
if(x<1||x>n)return;
if(p[x])Change(x,-p[x]);
if(a[x]){
ll L=(x-Left(1,x-1)-1),R=(Right(1,x+1)-x-1);
p[x]=(L+1)*(R+1)-1;
}
else{
ll L=(x-Left(0,x-1)-1),R=(Right(0,x+1)-x-1);
p[x]=Calc(L,R)+Count(R);
}
if(x<n&&a[x]!=a[x+1])p[x]--;
Change(x,p[x]);
return;
}
ll Get(ll x,ll l,ll r){
ll L=max(Left(0,x-1),l-1),R=min(Right(0,x+1),r+1);
L=x-L-1;R=R-x-1;
return Calc(L,R);
}
ll Qet(ll x,ll l,ll r){
ll L=max(Left(1,x-1),l-1),R=min(Right(1,x+1),r+1);
L=x-L-1;R=R-x-1;
return (L+1)*(R+1)-1;
}
signed main()
{
scanf("%lld",&n);
s[0].insert(0);s[0].insert(n+1);
s[1].insert(0);s[1].insert(n+1);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]),s[a[i]].insert(i);
for(ll i=1;i<=n;i++)
Updata(i);
scanf("%lld",&m);
while(m--){
ll op,l,r,x;
scanf("%lld",&op);
if(op==1){
scanf("%lld",&x);
s[a[x]].erase(x);
a[x]=!a[x];
s[a[x]].insert(x);
Updata(x);
Updata(Left(0,x-1));
Updata(Left(1,x-1));
Updata(Right(0,x+1));
Updata(Right(1,x+1));
}
else{
scanf("%lld%lld",&l,&r);
ll ans=(r-l+1)*(r-l+2)/2;
if(Left(1,r)<l){printf("%lld\n",ans);continue;}
if(Left(0,r)<l){ans-=Count(r-l+1);printf("%lld\n",ans);continue;}
ans-=Ask(r)-Ask(l-1);
if(r<n&&a[r]!=a[r+1])ans--;
ll Ll=Left(0,l-1),Rr=Right(0,r+1),Lr=Left(0,r),Rl=Right(0,l);
ans=ans+Get(Rl,1,n)-Get(Rl,l,r);
if(Lr!=Rl)ans=ans+Get(Lr,1,n)-Get(Lr,l,r);
if(a[r+1])ans=ans+Count(Rr-Lr-1)-Count(r-Lr);
if(a[l])ans=ans-Count(Rl-l);
Ll=Left(1,l),Rr=Right(1,r),Lr=Left(1,r),Rl=Right(1,l);
ans=ans+Qet(Rl,1,n)-Qet(Rl,l,r);
if(Lr!=Rl)ans=ans+Qet(Lr,1,n)-Qet(Lr,l,r);
// if(!a[r])ans=ans+Caunt(Rr-Rl-1)-Caunt(r-Rl);
// if(!a[l])ans=ans-Caunt(Lr-l);
printf("%lld\n",ans);
}
}
return 0;
}
P4428-[BJOI2018]二进制【树状数组,set】的更多相关文章
- BZOJ4888 [Tjoi2017]异或和 FFT或树状数组+二进制拆位
题面 戳这里 简要题解 做法一 因为所有数的和才100w,所以我们可以直接求出所有区间和. 直接把前缀和存到一个权值数组,再倒着存一遍,大力卷积一波. 这样做在bzoj目前还过不了,但是luogu开O ...
- BIT 树状数组 详解 及 例题
(一)树状数组的概念 如果给定一个数组,要你求里面所有数的和,一般都会想到累加.但是当那个数组很大的时候,累加就显得太耗时了,时间复杂度为O(n),并且采用累加的方法还有一个局限,那就是,当修改掉数组 ...
- POJ2828 Buy Tickets[树状数组第k小值 倒序]
Buy Tickets Time Limit: 4000MS Memory Limit: 65536K Total Submissions: 19012 Accepted: 9442 Desc ...
- UVA11525 Permutation[康托展开 树状数组求第k小值]
UVA - 11525 Permutation 题意:输出1~n的所有排列,字典序大小第∑k1Si∗(K−i)!个 学了好多知识 1.康托展开 X=a[n]*(n-1)!+a[n-1]*(n-2)!+ ...
- 树状数组求第k小的元素
int find_kth(int k) { int ans = 0,cnt = 0; for (int i = 20;i >= 0;i--) //这里的20适当的取值,与MAX_VAL有关,一般 ...
- 51nod1019逆序数(归并排序/树状数组)
题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1019 题意:中文题诶- 思路: 方法1:归并排序- 归并排序过 ...
- POJ 3067 Japan(经典树状数组)
基础一维树状数组 题意:左边一排 1-n 的城市,右边一排 1-m 的城市,都从上到下依次对应.接着给你一些城市对,表示城市这两个城市相连,最后问你一共有多少个交叉,其中处于城市处的交叉不算并且每个 ...
- 【转载】区间信息的维护与查询(一)——二叉索引树(Fenwick树、树状数组)
在网上找到一篇非常不错的树状数组的博客,拿来转载,原文地址. 树状数组 最新看了一下区间的查询与修改的知识,最主要看到的是树状数组(BIT),以前感觉好高大上的东西,其实也不过就这么简单而已. 我们有 ...
- (新人的第一篇博客)树状数组中lowbit(i)=i&(-i) 的简单文字证明
第一次写博好激动o(≧v≦)o~~初一狗语无伦次还请多多指教 先了解树状数组http://blog.csdn.net/int64ago/article/details/7429868感觉这个前辈写 ...
随机推荐
- GC垃圾收集器
垃圾收集器是垃圾收集算法的具体实现,是执行垃圾收集算法的,是守护线程. HotSport虚拟机采用分代收集(JVM规范并未对堆区进行划分),将堆分为年轻代和老年代,垃圾收集器也是这样组合使用的,不过已 ...
- C# 中的异步问题 Task
public class SharedData { public int Value { get; set; } } public class Test { async Task ModifyValu ...
- Redis脑图
转自:http://alphawang.com/blog/redis-mind-map/ 最近梳理了下 Redis 知识图谱,画了个脑图,涵盖了 Redis 数据类型.持久化机制.主从.哨兵.集群.应 ...
- 地图控件:overview、scale、toolbar
地图常用控件: 1.AMap.MapType:地图类型切换插件,用来切换固定的几个常用图层 2.AMap.OverView:地图鹰眼插件,默认在地图右下角显示缩略图 3.AMap.Scale:地图比例 ...
- Nginx从安装到虚拟主机、https加密、重定向的设置
编译前的设置: 在源代码文件中把版本号注释掉,这是为了防止针对特定版本的恶意攻击 关闭编译时的调试模式 解决编译前的依赖性 进行配置参数: 对参数进行解读: 编译和安装: 做软链接方便调用: 创建ng ...
- 【Azure 应用服务】Python flask 应用部署在Aure App Service 遇见的 3 个问题
在App Service(Windows)中部署Flask应用时的注意事项: ● 添加Python扩展插件,Python 3.6.4 x64: ●● 配置 FastCGI 处理程序,添加Web.con ...
- 机械硬盘换到SSD后系统引导报错代码0xc000000e
由于机械硬盘IO不够用,系统使用起来非常的缓慢,特意购买了新的SSD进行了替换.机械硬盘的IO在70左右,SSD的IO在1000-4000左右指普通消费SSD. 由于不想安装系统,就直接把机械硬盘的数 ...
- Tars | 第4篇 Subset路由规则业务分析与源码探索
目录 前言 1. Subset不是负载均衡 1.1 任务需求 1.2 负载均衡源码结构图 1.3 负载均衡四种调用器 1.4 新增两种负载均衡调用器 1.5 Subset应该是"过滤&quo ...
- 20210816 你相信引力吗,marshland,party?,半夜
考场 第一眼都不可做 T1 长得就像单调栈/单调队列,推了推性质发现优弧.劣弧都合法的点对很好处理,其他情况只在一种情况合法,那么开两个单调队列分别统计距离 \(\le\frac2n,>\fra ...
- Spring事务管理回滚问题
Spring事务管理不能回滚问题 在前段时间学习SpringMVC的练习中,碰到声明式事务管理时,事务不能回滚的情况,通过查看博客和资料,解决了问题. 原因 导致Spring事务管理不能回滚的原因有两 ...