【学习笔记】四毛子算法/ 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\) 组再做就行了

posted @ 2020-11-07 10:13  yspm  阅读(2131)  评论(5编辑  收藏  举报