1. 程式人生 > 其它 >#線段樹#洛谷 4428 [BJOI2018]二進位制

#線段樹#洛谷 4428 [BJOI2018]二進位制

線段樹

題目

有一個長為 \(n\) 的二進位制串,支援單個位置取反,對於這個二進位制串的一個子區間,
求出其有多少位置不同的連續子串,滿足在重新排列後(可包含前導0)是一個 3 的倍數。


分析

考慮對於單個位置\(2^i\bmod 3\)\(1,2,1,2,\cdots\)
3的倍數有很多種情況,考慮補集轉化,
設0的個數為\(c0\),1的個數為\(c1\),則不是3的倍數當且僅當

\[\begin{cases}c1=1\&c0>1\\c1 \bmod 2==1\&c0\leq 1\end{cases} \]

由於支援單點修改,所以還需要一個線段樹,
一個區間的答案等於左右區間的答案加上合併後多出的答案

一條一條考慮,上面這個只考慮\(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;
}