#線段樹#洛谷 4428 [BJOI2018]二進位制
阿新 • • 發佈:2021-07-12
線段樹
由於支援單點修改,所以還需要一個線段樹,
題目
有一個長為 \(n\) 的二進位制串,支援單個位置取反,對於這個二進位制串的一個子區間,
求出其有多少位置不同的連續子串,滿足在重新排列後(可包含前導0)是一個 3 的倍數。
分析
考慮對於單個位置\(2^i\bmod 3\)為\(1,2,1,2,\cdots\)
3的倍數有很多種情況,考慮補集轉化,
設0的個數為\(c0\),1的個數為\(c1\),則不是3的倍數當且僅當
由於支援單點修改,所以還需要一個線段樹,
一個區間的答案等於左右區間的答案加上合併後多出的答案
一條一條考慮,上面這個只考慮\(c1=1\),再減掉\(c0\leq 1\)的情況
單考慮\(c1=1\),即需要維護前後綴0的最長長度\(l0,r0\),
為了少判斷,還需要維護\(l1,r1\)表示只有1個1的前後綴個數
那合併後多出的答案就是\(lson.r1*rson.l0+lson.r0+rson.l1\)
然後對於\(c0=1\)的情況當且僅當一個0和一個1相鄰,那麼合併的時候直接判斷即可
對於\(c0=0\)的情況由於下一種情況被放到上面處理所以沒有重複
對於\(c1\bmod 2==1\&c0\leq 1\)的情況,
設\(f[0/1][0/1]\)表示字首或字尾0的個數為0或1且1的個數為偶數或奇數,直接轉移即可
程式碼
#include <cstdio> #include <cctype> #include <cstring> #define rr register using namespace std; const int N=100011; typedef long long lll; int n,a[N]; struct rec{int lo,ro,co,cz,lz,rz,f[2][2],g[2][2]; lll w;}b[N<<2]; inline signed iut(){ rr int ans=0; rr char c=getchar(); while (!isdigit(c)) c=getchar(); while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar(); return ans; } inline void print(lll ans){ if (ans>9) print(ans/10); putchar(ans%10+48); } inline rec pup(rec t0,rec t1){ rr rec t; t.lz=t0.lz+(t0.co?0:t1.lz),t.rz=t1.rz+(t1.co?0:t0.rz); t.co=t0.co+t1.co,t.cz=t0.cz+t1.cz; t.lo=t0.lo+(t0.co?(t0.co==1?t1.lz:0):t1.lo); t.ro=t1.ro+(t1.co?(t1.co==1?t0.rz:0):t0.ro); for (rr int i=0;i<2;++i) for (rr int j=0;j<2;++j){ t.f[i][j]=t0.f[i][j]+(i>=t0.cz?t1.f[i-t0.cz][(j^t0.co)&1]:0); t.g[i][j]=t1.g[i][j]+(i>=t1.cz?t0.g[i-t1.cz][(j^t1.co)&1]:0); } t.w=t0.w+t1.w+1ll*t0.rz*t1.lo+1ll*t0.ro*t1.lz; for (rr int i0=0;i0<2;++i0) for (rr int i1=0;i1<2;++i1) if (i0+i1<2) t.w+=1ll*t0.g[i0][0]*t1.f[i1][1]+1ll*t0.g[i0][1]*t1.f[i1][0]; if ((!t0.rz&&t1.lz)||(t0.rz&&!t1.lz)) --t.w; return t; } inline void pdown(rec &t,int x){ memset(t.f,0,sizeof(t.f)); memset(t.g,0,sizeof(t.g)); if (x) t.lo=t.ro=t.co=t.w=1,t.cz=t.lz=t.rz=0,t.f[0][1]=t.g[0][1]=1; else t.lo=t.ro=t.co=t.w=0,t.cz=t.lz=t.rz=1,t.f[1][0]=t.g[1][0]=1; } inline void build(int k,int l,int r){ if (l==r){pdown(b[k],a[l]); return;} rr int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); b[k]=pup(b[k<<1],b[k<<1|1]); } inline void update(int k,int l,int r,int x){ if (l==r){pdown(b[k],a[l]); return;} rr int mid=(l+r)>>1; if (x<=mid) update(k<<1,l,mid,x); else update(k<<1|1,mid+1,r,x); b[k]=pup(b[k<<1],b[k<<1|1]); } inline rec query(int k,int l,int r,int x,int y){ if (l==x&&r==y) return b[k]; rr int mid=(l+r)>>1; if (y<=mid) return query(k<<1,l,mid,x,y); else if (x>mid) return query(k<<1|1,mid+1,r,x,y); else return pup(query(k<<1,l,mid,x,mid),query(k<<1|1,mid+1,r,mid+1,y)); } signed main(){ n=iut(); for (rr int i=1;i<=n;++i) a[i]=iut(); build(1,1,n); for (rr int T=iut();T;--T){ rr int opt=iut(); if (opt==1){ rr int x=iut(); a[x]^=1,update(1,1,n,x); }else{ rr int l=iut(),r=iut(); rr rec t=query(1,1,n,l,r); rr lll ans=1ll*(r-l+1)*(r-l+2)>>1; print(ans-t.w),putchar(10); } } return 0; }