日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/3 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 P4428-[BJOI2018]二进制【树状数组,set】 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

正題

題目鏈接:https://www.luogu.com.cn/problem/P4428


題目大意

長度為nnn0/10/10/1串要求支持

  • 修改一個位置
  • 求區間[l,r][l,r][l,r]有多少個子區間重排后的二進制數可以被三整除
  • 1≤n≤1051\leq n\leq 10^51n105


    解題思路

    首先有22k%3=1(k∈Z)2^{2k}\%3=1(k\in Z)22k%3=1(kZ)22k+1%3=2(k∈Z)2^{2k+1}\%3=2(k\in Z)22k+1%3=2(kZ)
    分三種情況考慮

    • 111111那么顯然無論如何都不可以被三整除
    • 2k2k2k111那么我們之間都排在最后面就好了。
    • 2k+12k+12k+1111kkk不能為000),那么有一種方案就是把某個在奇數位置的111放到偶數位置就可以了,此時需要區間的長度至少為2k+32k+32k+3

    然后具體分析一下相當于一個區間111的個數不能為111且如果是奇數個那么必須至少有兩個000

    看起來很復雜可以反過來做分成以下情況

  • 區間全是111且長度為奇數
  • 區間有一個000且長度為偶數
  • 區間只有一個111
  • 由于222333會重復一種只有一個111和一個000的情況所以需要加回這個方案
  • 第四種是最好維護的,順便用樹狀數組記錄就好了

    然后前三種我們對于0/10/10/1的位置分別開一個setsetset來查詢某個位置前驅/后繼的0/1。

    然后第三種情況我們對于每個111考慮左右的000區間然后記錄在樹狀數組111的位置

    對于第二種情況我們考慮對于每個000考慮左右的111然后記錄在那個000的位置

    對于第一種情況我們之間記錄到區間最左端的000處。

    然后統計答案的時候要記得把邊界的情況考慮

    寫起來有點麻煩

    時間復雜度O(nlog?n)O(n\log n)O(nlogn)


    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】的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。