P4428-[BJOI2018]二进制【树状数组,set】

1|0正题

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


1|1题目大意

长度为n0/1串要求支持

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

1n105


1|2解题思路

首先有22k%3=1(kZ)22k+1%3=2(kZ)
分三种情况考虑

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

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

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

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

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

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

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

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

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

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

写起来有点麻烦

时间复杂度O(nlogn)


1|3code

#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; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/15002436.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(106)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示