【學習筆記】四毛子算法/ The Method of Four Russians


The Method of Four Russians

有些分塊問題塊間信息合並非常耗時,這算法主要是用 \(\rm{ST}\) 表或者線段樹來優化這個過程

例題

upd on 2021-09-19

\(\rm{O(n)-O(1)\ RMQ}\)

本質上是差分序列只有 \(\pm 1\) 的序列的 \(\rm{RMQ}\) 問題

對序列建出 笛卡爾 樹后則問題轉化為一個 \(LCA\) 問題

使用歐拉序 \(\rm{+ST}\) 表來處理本問題

因為在歐拉序中,相鄰的點深度差為 \(\pm 1\),那么設一個塊的大小為 \(b=\frac{\log_2 n}2\)

那么一個段的 \(\pm 1\) 一共有 \(2^b\),預處理需要掃描整個段,復雜度 \(\Theta(b2^b)\)

對於段之間的,使用四毛子算法的思想,用 \(\rm{ST}\) 表快速合並,預處理復雜度 \(\Theta(\frac nb \log \frac nb)\)

這兩部分均小於 \(\Theta(n)\)

對於小段的處理需要先把塊的 \(\pm 1\) 表示左移取 \(\rm{and}\) 之后再計算

LOJ6499顏色

分塊,然后用 \(\rm{bitset}\) 處理每個塊內出現的顏色種類

因為顏色數量非常大,\(\rm{bitset}\) 做一次 或 操作非常耗費時間

那么可以考慮用 \(\rm{ST}\) 表或者線段樹優化這個過程,也就是把塊中間的信息先合並上

#include<bits/stdc++.h>
using namespace std;
#define reg register
#define int long long
#define rep(i,a,b) for(reg int i=a;i<=b;++i)
const int N=1e5+10,P=(N>>5)+10,SZ=(1<<16)+1;
int b[N],c,a[N],n,T,p,bl[N],block,ct[SZ];
struct BITSET{
	unsigned int a[P];
	BITSET operator |(BITSET tp) const{BITSET res; rep(i,0,c) res.a[i]=a[i]|tp.a[i];return res;}  
	inline void clear(){rep(i,0,c) a[i]=0; return ;}
	inline void set(int x){a[x>>5]|=1u<<(x&31); return ;}
	inline int count(){int res=0; rep(i,0,c) res+=ct[a[i]>>16]+ct[a[i]&65535]; return res;}
}ans,f[80][9];
inline void work(int l,int r){
	if(bl[l]+1>=bl[r]) rep(i,l,r) ans.set(a[i]);
	else{
		for(reg int i=l;bl[i]==bl[l];++i) ans.set(a[i]);
		for(reg int i=r;bl[i]==bl[r];--i) ans.set(a[i]);
		int t=log2(bl[r]-bl[l]-1);
		ans=ans|f[bl[l]+1][t]|f[bl[r]-(1<<t)][t];
	} return ;
}
int lans,k,l,r;
signed main(){
	n=read(); T=read(); p=read(); block=sqrt(1.2*log2(n+1)*n);
	for(reg int i=1;i<=65535;++i) ct[i]=ct[i>>1]+(i&1);
	rep(i,1,n) bl[i]=i/block+1,b[i]=a[i]=read();
	sort(b+1,b+n+1); c=unique(b+1,b+n+1)-b-1;
	rep(i,1,n) f[bl[i]][0].set(a[i]=lower_bound(b+1,b+c+1,a[i])-b;);
	c>>=5;
	for(reg int j=1;(1<<j)<=bl[n];++j){
		for(reg int i=1;i+(1<<j)-1<=bl[n];++i){
			f[i][j]=f[i][j-1]|f[i+(1<<(j-1))][j-1];
		}
	}
	bool fl=0;
	while(T--){
		ans.clear(); k=read();
		while(k--){
			int l=read(),r=read();
			if(p&&fl) l=(l^lans)%n+1,r=(r^lans)%n+1;
			if(r<l) swap(l,r);
			work(l,r);
		}fl=1;
		printf("%d\n",lans=ans.count()); 
	} return 0;
}

不過這代碼里面塊的大小是嫖的……

牛客練習賽72F

答案容斥完了就是所有顏色減去 \(dfn\) 序不在那個區間里面的情況

所以沖一個四毛子上去即可

但是卡空間,那么把詢問分成 \(10\) 組再做就行了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM