浅谈 bitset 在数据结构中的一些应用(续)

这是上一篇

注:若未说明则数据范围小于等于 \(10^5\)

1

题目

qoj#1851. Directed Acyclic Graph

题面

\(n\) 个点 \(m\) 条边的 DAG,点有点权。\(q\) 次操作:每次操作将 \(u\) 能到达的点的点值 \(val_v\) 变成 \(x\)\(\min(val_v,x)\) 或查询 \(u\) 的点权(\(x\le10^9\),5s)。

做法

lxl 说这题比追忆难。

这种 DAG 可达性基本上都可以这样操作分块:每 \(w(w=64)\) 次操作为一组,复杂度做到 \(O(\frac{q(n+m)}{w})\)。好处是空间很小,且只需要用单个 unisnged long long 维护一个点的可达性,更好写;坏处是这个常数往往会大一些。

本题也是如此:每 \(w\) 此操作为一组,分别维护最小值赋值以及所有赋值的可达性。注意一下细节:例如最小值赋值的可达性需将所有值排序。

代码:

#include<bits/stdc++.h>
using namespace std;
constexpr int N=1e5+5,B=64;
struct node2{int op,u,x,id;};
vector<int>e[N];
vector<node2>d,d2;
unsigned long long as[N],mi[N],cas[B+2];
int n,m,q,eu[N],ev[N],du[N],a[N],cmi[B+2];
inline void work(){
	int siz=d.size();unsigned long long now=0,ast=0;
	d2=d,sort(d2.begin(),d2.end(),[](node2&x,node2&y){return x.x<y.x;});
	for(int i=0;i<siz;++i)cmi[d2[i].id]=i;
	for(int i=siz-1;~i;--i){
		if(d[i].op==1)as[d[i].u]|=(1ull<<i),cas[i]=now,ast|=(1ull<<i);
		if(d[i].op==2)as[d[i].u]|=(1ull<<i),mi[d[i].u]|=(1ull<<cmi[i]),now|=(1ull<<cmi[i]);
	}
	for(int i=1;i<=m;++i)as[ev[i]]|=as[eu[i]],mi[ev[i]]|=mi[eu[i]];
	for(int i=0,ans,j;i<siz;++i){
		if(d[i].op==3){
			for(ans=a[d[i].u],j=0;j<i;++j){
				if(d[j].op==1&&((as[d[i].u]>>j)&1))ans=d[j].x;
				if(d[j].op==2&&((as[d[i].u]>>j)&1))ans=min(ans,d[j].x);
			}cout<<ans<<'\n';
		}
	}
	for(int i=1,c;i<=n;++i){
		if((as[i]&=ast))a[i]=d[c=__lg(as[i])].x,mi[i]&=cas[c];
		if(mi[i])a[i]=min(a[i],d2[__builtin_ctzll(mi[i])].x);
	}
	d.clear();memset(as,0,sizeof(as)),memset(mi,0,sizeof(mi));
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
	cin>>n>>m>>q;
	for(int i=1,u,v;i<=m;++i)cin>>u>>v,e[u].emplace_back(v),++du[v];
	queue<int>qu;
	for(int i=1;i<=n;++i)if(!du[i])qu.push(i);
	for(int k=0,u;!qu.empty();){
		u=qu.front(),qu.pop();
		for(auto v:e[u])eu[++k]=u,ev[k]=v,(--du[v])?void():qu.push(v); 
	}
	for(int i=1,op,u=0,x=0,now=0;i<=q;++i){
		cin>>op;(op<=2)?cin>>u>>x:(x=0x3f3f3f3f,cin>>u);
		d.push_back({op,u,x,now});
		if(now==B-1||i==q)work(),now=0;else++now;
	}
	return 0;
}

2

题目

P5355 [Ynoi Easy Round 2017] 由乃的玉米田

题面

长度为 \(n\) 的序列 \(a\),有 \(m\) 次操作,每次询问区间内是否存在两个数使得和、差、积或商为 \(x\)(128MB,本处要求强制在线)。

做法

加减乘可按前面的思想块间建立 ST 表做到空间复杂度 \(O(\frac{n\sqrt n\log n}{w})\),时间复杂度 \(O(n\sqrt n+\frac{n^2}{w})\)(设 \(n,q,V\) 同阶),这也适用于 \(x>\sqrt V\) 的除法部分。

对于 \(x\le\sqrt V\) 的除法部分,提前预处理这部分的答案,设 \(sr_{x,l}\) 表示满足左端点为 \(l\) 的最小符合题意的区间(这里规定无论如何也无法满足时右端点为 \(n+1\)),双指针处理可做到空间复杂度 \(O(n\sqrt n)\),时间复杂度 \(O(n\sqrt n)\)

兴许使用 short 能过,但其实这部分可以做到空间复杂度 \(O(\frac{n\sqrt n}{w})\) 的。

我们对于每个 \(x\) 开一个长度为 \(2n+1\) 的 bitset,对于每个 \(l\),将第 \(l+sr_{x,l}-1\) 位设为 \(1\)\(sr_{x,l}\)\(l\) 上升而单调不降,所以 \(l+sr_{x,l}-1\)\(l\) 单调上升。我们有:如果这一位为 \(1\),设其前缀 \(1\) 的个数为 \(s_1\),前缀 \(0\) 的个数为 \(s_0\),则 \(sr_{x,s_1}=s_0\)。我们查询时要找到最小的 \(c\) 使得其前缀 \(1\) 的个数为 \(l\),即可求得 \(sr_{x,l}=c-l+1\)。我们使用前缀和加上二分可以做到单次查询 \(O(\log n)\)

最终时间复杂度为 \(O(n\sqrt n+\frac{n^2}{w})\),空间复杂度为 \(O(\frac{n\sqrt n\log n}{w})\)。也是使用了不到 64MB 空间在线地通过了本题:

constexpr int N=1e5;
constexpr int block=420,B=317;
int n,m,a[N+2],bl[N+2],sum0[N+2],L[N/block+2],R[N/block+2],sum[N+2],blcnt;
bitset<N+2>zh[11][N/block+2],fu[11][N/block+2],s;
struct bitst{
	#define pcnt __builtin_popcountll
	unsigned long long a[3200];
	inline void flip(int x)noexcept{a[x>>6]^=(1ull<<(x&63));}
	unsigned int sfsadf[3201];unsigned int*ans=sfsadf+1;
	inline void work(){
		for(int i=0,p=0;i<3200;i+=8){
			ans[i]=pcnt(a[i])+p,ans[i^1]=pcnt(a[i^1]),
			ans[i^2]=pcnt(a[i^2]),ans[i^3]=pcnt(a[i^3]),
			ans[i^4]=pcnt(a[i^4]),ans[i^5]=pcnt(a[i^5]),
			ans[i^6]=pcnt(a[i^6]),ans[i^7]=pcnt(a[i^7]),
			ans[i^1]+=ans[i  ],ans[i^2]+=ans[i^1],ans[i^3]+=ans[i^2],
			ans[i^4]+=ans[i^3],ans[i^5]+=ans[i^4],ans[i^6]+=ans[i^5],
			ans[i^7]+=ans[i^6],p=ans[i^7];
		}
	}
	inline int calc(int x)noexcept{return ans[(x>>6)-1]+pcnt(a[x>>6]<<(63^(x&63)));}
	inline int operator[](int x){
		int l=0,r=3200<<6;
		while(l<=r){
			int mid=(l+r)>>1;
			if(calc(mid)<x)l=mid+1;
			else r=mid-1;
		}return l-x+1;
	}
}sl[B+2];
inline bitset<N+2>zq(int l,int r){int k=__lg(r-l+1);return zh[k][l]|zh[k][r-(1<<k)+1];}
inline bitset<N+2>fq(int l,int r){int k=__lg(r-l+1);return fu[k][l]|fu[k][r-(1<<k)+1];}
inline bitset<N+2>zquery(int l,int r){
	if(bl[l]==bl[r]){s.reset();for(int i=l;i<=r;++i)s.set(a[i]);return s;}
	if(bl[l]+1==bl[r])s.reset();else s=zq(bl[l]+1,bl[r]-1);
	for(int i=R[bl[l]];i>=l;--i)s.set(a[i]);
	for(int i=L[bl[r]];i<=r;++i)s.set(a[i]);
	return s;
}
inline bitset<N+2>fquery(int l,int r){
	if(bl[l]==bl[r]){s.reset();for(int i=l;i<=r;++i)s.set(N-a[i]);return s;}
	if(bl[l]+1==bl[r])s.reset();else s=fq(bl[l]+1,bl[r]-1);
	for(int i=R[bl[l]];i>=l;--i)s.set(N-a[i]);
	for(int i=L[bl[r]];i<=r;++i)s.set(N-a[i]);
	return s;
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=block;++i)bl[i]=1;
	for(int i=block+1;i<=n;++i)bl[i]=bl[i-block]+1;blcnt=bl[n];
	for(int i=1;i<=n;++i)cin>>a[i];
	for(int i=1;i<=n;++i)sum0[i]=sum0[i-1]+(a[i]==0);
	for(int i=1;i<=blcnt;++i){
		L[i]=(i-1)*block+1,R[i]=min(i*block,n);
		for(int j=L[i];j<=R[i];++j)
		zh[0][i].set(a[j]),fu[0][i].set(N-a[j]);
	}
	for(int i=1;(1<<i)<=blcnt;++i){
		for(int j=1;j+(1<<i)-1<=blcnt;++j){
			zh[i][j]=zh[i-1][j]|zh[i-1][j+(1<<(i-1))],
			fu[i][j]=fu[i-1][j]|fu[i-1][j+(1<<(i-1))];
		}
	}
	for(int i=1;i<=B;++i){
		memset(sum,0,sizeof(sum));
		for(int l=1,r=1;l<=n;){
			while(r<=n){
				++sum[a[r]];
				if((a[r]%i==0&&sum[a[r]/i])||(a[r]*i<=N&&sum[a[r]*i]))break;
				++r;
			}
			if(r>n){for(r=n+1;l<=n;++l)sl[i].flip(l+r-1);break;}
			while(l<=r){
				if((a[r]%i==0&&sum[a[r]/i])||(a[r]*i<=N&&sum[a[r]*i]))sl[i].flip(l+r-1),--sum[a[l++]];
				else break;
			}
			++r;
		}sl[i].work();
	}
	for(int i=1,ans=0,op,l,r,x;i<=m;++i){
		ans=0,cin>>op>>l>>r>>x;
		if(op==1){s=zquery(l,r);if((s&(s<<x)).any())ans=1;
		}else if(op==2){if((zquery(l,r)&(fquery(l,r)>>(100000-x))).any())ans=1;
		}else if(op==3){
			if(x==0)ans=(sum0[r]-sum0[l-1]>0);
			else{
				s=zquery(l,r);
				for(int j=1;j*j<=x;++j)if(x%j==0&&s[j]&&s[x/j]){ans=1;break;}
			}
		}else if(x>B){
			s=zquery(l,r);
			for(int j=1,ksum=x;ksum<=100000;ksum+=x,++j)if(s[j]&&s[ksum]){ans=1;break;}
		}else if(x==0)ans=(sum0[r]-sum0[l-1]>0)&&(sum0[r]-sum0[l-1]<r-l+1);
		else ans=(sl[x][l]<=r);
		cout<<(ans?"yuno":"yumi")<<'\n';
	}
	return 0;
}

3

题目

P5313 [Ynoi2011] WBLT

题面

长度为 \(n\) 的序列 \(a\)\(m\) 次查询 \(l,r,b\),需输出最大的 \(x\),使得存在一个 \(a\),满足 \(0\leq a<b\),使得 \(a,a+b,a+2b,\ldots,a+(x-1)b\) 都在区间 \([l,r]\) 内至少出现过一次,若不存在输出 \(0\)(1.5s)。

做法

考虑莫队加上 bitset:若 \(b<w\),则对于每个 \(b\) 都跑一遍莫队;若 \(b\ge w\),则直接对于这部分跑莫队,得出的 bitset 将其分裂成 \(\frac{V}{b}\) 个依次进行按位与,总复杂度是 \(O(n\sum_{i=1}^{w-1}\sqrt{m_i}+\frac{nV}{w})=O(n\sqrt{mw}+\frac{nV}{w})\)

然而本题困难的地方在于手写 bitset

首先说明一点:可能是讨论区有人说 vector 比数组快的缘故导致大家都用 vector 写,其实是他可能写得劣一些。接下来,给大家说一种比较好写的方法。

首先是一些基础的:

struct bitst{
	unsigned long long a[1600];int siz=1580;
	inline void resize0(const int x){//将前 x 位都设为 0
		siz=(x+63)>>6;
		__builtin_memset(a,0,8*siz);
	}
	inline void resize1(const int x){//将前 x 位都设为 1
		siz=(x+63)>>6;
		__builtin_memset(a,0xff,8*siz);
		(x&63)?(a[siz-1]>>=64-(x&63)):1;//注意一下这里的 1 用到哪才将 1 赋值到哪,这样可以减少接下来的一些特判。
	}
	inline void flip(int x){
		a[x>>6]^=(1ull<<(x&63));
	}
	inline int mex(){//求第一个 0 的位置
		for(int i=0;;++i)
			if(~a[i])//__builtin_ctzll(x) 表示的是__lg(lowbit(x))
				return(i<<6)|__builtin_ctzll(~a[i]);
	}
}

困难的地方在于 split 处。对于我的实现来讲需要特判一下左移 \(64\) 位的 UB 还有右端点的取值。

inline int split(const bitst&a,int mod){
	b.resize1(mod);const int asiz=a.siz<<6;int ans=0;
	for(int l=0,f=1;l<asiz;l+=mod,++ans,f=1){
		const int l6=l>>6;
		const int r=min(b.siz,a.siz-l6);//注意一下右端点,小心越界。
		const int l63=l&63;
		const int _l63=64-(l&63);
		if(!l63)for(int i=0;i<r;++i)
			b.a[i]&=a.a[i+l6];//特判左移 64 位的 UB。
		else for(int i=0;i<r;++i)
			b.a[i]&=(a.a[i+l6]>>l63)|(a.a[i+l6+1]<<_l63);
		for(int i=0;i<r;++i)//不能枚举到 b.siz,因为我们只处理了前 r 个 unsigned long long。
			if(b.a[i]){f=0;break;}
		if(f)return ans;
	}return ans;
}

最后还有一点:莫队的块长一定要取 \(\frac{n}{\sqrt m_i}\),要不然复杂度就假了。

总的代码:

#include<bits/stdc++.h>
using namespace std;
constexpr int N=1e5+5;
struct bitst{
	unsigned long long a[1600];int siz=1580;
	inline void resize0(const int x){return siz=(x+63)>>6,__builtin_memset(a,0,sizeof(unsigned long long)*siz),void();}
	inline void resize1(const int x){return siz=(x+63)>>6,__builtin_memset(a,0xff,sizeof(unsigned long long)*siz),
	(x&63)?(a[siz-1]>>=64-(x&63)):1,void();}
	inline void flip(int x){a[x>>6]^=(1ull<<(x&63));}
	inline int mex(){for(int i=0;;++i)if(~a[i]){return(i<<6)|__builtin_ctzll(~a[i]);}}
}b,t1,t2[64];
inline int split(const bitst&a,int mod){
	b.resize1(mod);const int asiz=a.siz<<6;int ans=0;
	for(int l=0,f=1;l<asiz;l+=mod,++ans,f=1){
		const int l6=l>>6,r=min(b.siz,a.siz-l6),l63=l&63,_l63=64-(l&63);
		if(!l63)for(int i=0;i<r;++i)b.a[i]&=a.a[i+l6];
		else for(int i=0;i<r;++i)b.a[i]&=(a.a[i+l6]>>l63)|(a.a[i+l6+1]<<_l63);
		for(int i=0;i<r;++i)if(b.a[i]){f=0;break;}
		if(f)return ans;
	}return ans;
}
struct node{int l,r,x,id;};
int n,m,a[N],ans[N],block,cnt[N],mo,lef=1,rig=0;
vector<node>e[64];
inline bool cmp(const node&x,const node&y){
	return(x.l/block!=y.l/block)?(x.l<y.l):(((x.l/block)&1)?(x.r>y.r):(x.r<y.r));}
#define add(x) (cnt[x]++)?void():t1.flip(x)
#define del(x) (--cnt[x])?void():t1.flip(x)
#define ad2(x) (cnt[x]++)?void():t2[x%mo].flip(x/mo)
#define de2(x) (--cnt[x])?void():t2[x%mo].flip(x/mo)
int main(){
	cin>>n;for(int i=1;i<=n;++i)cin>>a[i];
	cin>>m;for(int i=1,l,r,x;i<=m;++i)cin>>l>>r>>x,e[(x>=64)?0:x].emplace_back(node{l,r,x,i});
	block=n/sqrt(e[0].size()+1),sort(e[0].begin(),e[0].end(),cmp);
	for(auto it:e[0]){
		while(lef>it.l)--lef,add(a[lef]);
		while(rig<it.r)++rig,add(a[rig]);
		while(lef<it.l)del(a[lef]),++lef;
		while(rig>it.r)del(a[rig]),--rig;
		ans[it.id]=split(t1,it.x);
	}
	for(mo=1;mo<=63;++mo){
		__builtin_memset(cnt,0,sizeof(cnt));
		lef=1,rig=0;block=n/sqrt(e[mo].size()+1);
		sort(e[mo].begin(),e[mo].end(),cmp);
		for(int i=0;i<mo;++i)t2[i].resize0(N/mo+2);
		for(auto it:e[mo]){
			while(lef>it.l)--lef,ad2(a[lef]);
			while(rig<it.r)++rig,ad2(a[rig]);
			while(lef<it.l)de2(a[lef]),++lef;
			while(rig>it.r)de2(a[rig]),--rig;
			for(int i=0;i<mo;++i)ans[it.id]=max(ans[it.id],t2[i].mex());
		}
	}
	for(int i=1;i<=m;++i)cout<<ans[i]<<'\n';
	return 0;
}

4

题目

P4119 [Ynoi2018] 未来日记

题面

长度为 \(n\) 的序列 \(a\)\(m\) 次操作:每次将 \([l,r]\) 值为 \(x\) 的数变为 \(y\) 或查询 \([l,r]\) 中的第 \(k\) 小值(512MB)。

做法

考虑对于每个数维护一个位置的 bitset,每 \(C\) 个数维护一个位置的 bitset,如果不看空间可以手写 bitset 维护前缀 \(1\) 的个数,但是本题空间稍微有点紧。

不妨在每个数存在的位置个数小于等于 \(B\) 时用 vector 存,反之用 bitset 存。\(B\)\(C\) 都取 \(\sqrt n\) 左右即可通过。

我相信大家都会写 bitset

#include<bits/stdc++.h>
using namespace std;
#define pcnt __builtin_popcountll
constexpr int N=1e5+5;
constexpr int V=1e5;
constexpr int VB=350;
constexpr int VC=V/VB+5;
constexpr int B=400;
constexpr int BC=N/B+5;
struct bitst{
	unsigned long long a[1600];
	int sfsadf[1605];int*ans=sfsadf+1;
	inline void flip(int x)noexcept{a[x>>6]^=(1ull<<(x&63));}
	inline int calc(int x)noexcept{return ans[(x>>6)-1]+pcnt(a[x>>6]<<(63^(x&63)));}
	inline int calc(int l,int r){return calc(r)-calc(l-1);}
	inline void work(){
		for(int i=0,p=0;i<1600;i+=8){
			ans[i  ]=pcnt(a[i])+p,ans[i^1]=pcnt(a[i^1]),ans[i^2]=pcnt(a[i^2]),ans[i^3]=pcnt(a[i^3]),
			ans[i^4]=pcnt(a[i^4]),ans[i^5]=pcnt(a[i^5]),ans[i^6]=pcnt(a[i^6]),ans[i^7]=pcnt(a[i^7]),
			ans[i^1]+=ans[i  ],ans[i^2]+=ans[i^1],ans[i^3]+=ans[i^2],ans[i^4]+=ans[i^3],
			ans[i^5]+=ans[i^4],ans[i^6]+=ans[i^5],ans[i^7]+=ans[i^6],p=ans[i^7];
		}
	}inline int count(){return ans[1599];}
}b[BC+10],blv[VC+10];
int st[N],top,bl[N],cnt[N],n,m;
struct structure{
	bool typ;int id;vector<int>v;
	inline void insert(int x){v.insert(lower_bound(v.begin(),v.end(),x),x);}
	inline auto upper(int x){return upper_bound(v.begin(),v.end(),x);}
	inline int calc(int x){return typ?b[id].calc(x):(upper_bound(v.begin(),v.end(),x)-v.begin());}
	inline int calc(int l,int r){return calc(r)-calc(l-1);}
	inline void flip(int l=0,int r=0,int siz=0){
		if(typ==0){typ=1,id=st[top--];for(auto i:v)b[id].flip(i);b[id].work(),v.clear();}
		else{
			typ=0,v.reserve(siz);
			for(int i=0,c=0;i<=(l>>6);++i)if(b[id].a[i]){
				while(b[id].a[i])c=__builtin_ctzll(b[id].a[i]),b[id].a[i]^=1ull<<c,v.emplace_back((i<<6)|c);
			}for(int i=(r>>6),c=0;i<1600;++i)if(b[id].a[i]){
				while(b[id].a[i])c=__builtin_ctzll(b[id].a[i]),b[id].a[i]^=1ull<<c,v.emplace_back((i<<6)|c);
			}st[++top]=id;
		}
	}
}t[N];
inline void solve01(int l,int r,int x,int y){
	auto itl=t[x].upper(l-1),itr=t[x].upper(r);
	for(auto it=itl;it!=itr;++it)blv[bl[x]].flip(*it),blv[bl[y]].flip(*it),b[t[y].id].flip(*it);
	t[x].v.erase(itl,itr);b[t[y].id].work();
}inline void solve00(int l,int r,int x,int y){
	if(t[y].v.size()+t[x].calc(l,r)>B)return t[y].flip(),solve01(l,r,x,y);
	auto itl=t[x].upper(l-1),itr=t[x].upper(r);int tys=t[y].v.size();
	if(itr-itl+1<B/4){
		for(auto it=itl;it!=itr;++it)blv[bl[x]].flip(*it),blv[bl[y]].flip(*it),t[y].insert(*it);
	}else{
		for(auto it=itl;it!=itr;++it)blv[bl[x]].flip(*it),blv[bl[y]].flip(*it),t[y].v.emplace_back(*it);
		inplace_merge(t[y].v.begin(),t[y].v.begin()+tys,t[y].v.end());
	}t[x].v.erase(itl,itr);
}inline void solve11(int l,int r,int x,int y){
	int txs=b[t[x].id].count()-t[x].calc(l,r);
	bitst&b1=b[t[x].id],&b2=b[t[y].id];
	const unsigned long long xc=b1.a[l>>6]^(b1.a[l>>6]>>(l&63)<<(l&63)),yc=b1.a[r>>6]^(b1.a[r>>6]<<((63^r)&63)>>((63^r)&63));
	b1.a[l>>6]^=xc,b1.a[r>>6]^=yc;
	for(int i=(l>>6);i<=(r>>6);++i)blv[bl[x]].a[i]^=b1.a[i],blv[bl[y]].a[i]^=b1.a[i],b2.a[i]^=b1.a[i],b1.a[i]=0;
	b1.a[l>>6]^=xc,b1.a[r>>6]^=yc;b2.work();
	(txs<=B)?t[x].flip(l,r,txs):b1.work();
}inline void solve10(int l,int r,int x,int y){
	if(t[y].v.size()+t[x].calc(l,r)>B)return t[y].flip(),solve11(l,r,x,y);
	int txs=b[t[x].id].count()-t[x].calc(l,r);
	bitst&b1=b[t[x].id];
	const unsigned long long xc=b1.a[l>>6]^(b1.a[l>>6]>>(l&63)<<(l&63)),yc=b1.a[r>>6]^(b1.a[r>>6]<<((63^r)&63)>>((63^r)&63));
	b1.a[l>>6]^=xc,b1.a[r>>6]^=yc;
	for(int i=(l>>6),c=0;i<=(r>>6);++i){
		if(b1.a[i]){blv[bl[x]].a[i]^=b1.a[i],blv[bl[y]].a[i]^=b1.a[i];
			while(b1.a[i])c=__builtin_ctzll(b1.a[i]),b1.a[i]^=1ull<<c,t[y].insert((i<<6)|c);
		}
	}
	b1.a[l>>6]^=xc,b1.a[r>>6]^=yc;
	(txs<=B)?t[x].flip(l,r,txs):b1.work();
}
inline void change(int l,int r,int x,int y){
	if(t[x].typ==0&&t[y].typ==0)solve00(l,r,x,y);
	else if(t[x].typ==0&&t[y].typ==1)solve01(l,r,x,y);
	else if(t[x].typ==1&&t[y].typ==1)solve11(l,r,x,y);
	else if(t[x].typ==1&&t[y].typ==0)solve10(l,r,x,y);
	blv[bl[x]].work(),blv[bl[y]].work();
}
inline int query(int l,int r,int k){
	if(k>(r-l+1))return-1;
	int now=0;
	while((k-=blv[++now].calc(l,r))>0);
	k+=blv[now].calc(l,r),now=(now-1)*VB;
	while((k-=t[++now].calc(l,r))>0);
	return now;
}
signed main(){
	for(int i=0;i<BC;++i)st[++top]=i;
	for(int i=1;i<=V;++i)bl[i]=(i-1)/VB+1;
	cin>>n>>m;for(int i=1,x;i<=n;++i)cin>>x,t[x].v.emplace_back(i),blv[bl[x]].flip(i);
	for(int i=1;i<=bl[V];++i)blv[i].work();
	for(int i=1;i<=V;++i)if(t[i].v.size()>B)t[i].flip();
	for(int i=1,op,l,r,x,y;i<=m;++i)
	cin>>op>>l>>r,(op==1)?(cin>>x>>y,(x!=y)?change(l,r,x,y):(void())):(cin>>x,cout<<query(l,r,x)<<'\n',void());
	return 0;
}

5

题目

P5692 [MtOI2019] 手牵手走向明天

题面

长度为 \(n\) 的序列 \(a\)\(m\) 次操作:每次将 \([l,r]\) 值为 \(x\) 的数变为 \(y\) 或查询 \([l,r]\) 中值为 \(x\) 的位置与值为 \(y\) 的位置的差的绝对值的最小值,无解输出 \(-1\)(1.5s)。

做法

喜欢出 ds 题不带权的出题人们你们好呀,我是 bitset

跟上一题类似,依旧每个数用 bitset 维护其出现的位置。依旧空间不够,把 \(1\) 个序列拆成 \(4\) 个即可。

查询似乎不太好做,不妨更暴力一些,直接预处理 \(256\times256\) 的表作为内部的贡献,外面的贡献位运算即可,复杂度是 \(O(\frac{n^2}{\sqrt w})\) 的,加点剪枝即可通过。

代码:

#include <bits/stdc++.h>
using namespace std;
constexpr int N=1e5+5;
#define pcnt __builtin_popcountll
struct bitst{
	unsigned long long a[400];
	inline bitst operator=(const bitst&b)noexcept{__builtin_memcpy(a,b.a,sizeof(a));return*this;}
	inline void reset()noexcept{__builtin_memset(a,0,sizeof(a));}
	inline void set(int x)noexcept{a[x>>6]|=(1ull<<(x&63));}
	inline void flip(int x)noexcept{a[x>>6]^=(1ull<<(x&63));}
	inline bool operator[](int x)noexcept{return (a[x>>6]>>(x&63))&1;}
	inline bool have(int x)noexcept{return (a[x>>6]>>(x&63))&1;}
	inline int count(int l,int r)noexcept{
		if((l>>6)==(r>>6))return pcnt((a[l>>6]>>(l&63))<<(63^(((r-l)&63))));
		int ans=pcnt(a[l>>6]>>(l&63))+pcnt(a[r>>6]<<(63^(r&63)));
		for(int i=(l>>6)+1;i<(r>>6);++i)ans+=pcnt(a[i]);
		return ans;
	}
	inline void flip(int l,int r)noexcept{
		if((l>>6)==(r>>6))return a[l>>6]^=(~0ull)>>(63^(((r-l)&63)))<<(l&63),void();
		a[l>>6]^=(~0ull)<<(l&63);a[r>>6]^=(~0ull)>>(63^(r&63));
		for(int i=(l>>6)+1;i<(r>>6);++i)a[i]=~a[i];
	}
	inline void set(int l,int r)noexcept{
		if((l>>6)==(r>>6))return a[l>>6]|=(~0ull)>>(63^(((r-l)&63)))<<(l&63),void();
		a[l>>6]|=(~0ull)<<(l&63);a[r>>6]|=(~0ull)>>(63^(r&63));
		for(int i=(l>>6)+1;i<(r>>6);++i)a[i]=~0ull;
	}
	inline void reset(int l,int r)noexcept{
		if((l>>6)==(r>>6))return a[l>>6]&=~((~0ull)>>(63^(((r-l)&63)))<<(l&63)),void();
		a[l>>6]&=~((~0ull)<<(l&63));a[r>>6]&=~((~0ull)>>(63^(r&63)));
		for(int i=(l>>6)+1;i<(r>>6);++i)a[i]=0;
	}
}b[N];
static int t[256][256]; 
static uint8_t calcl[256],calcr[256];
struct node{int op,l,r,x,y;}q[N];
int n,m,a[N],ans[N],rx[N],ry[N];
inline void work1(bitst&b1,bitst&b2,int l,int r)noexcept{
	if((l>>6)==(r>>6))return b2.a[l>>6]|=b1.a[l>>6]&((~0ull)<<((63^r)&63)>>((63^r)&63))&((~0ull)>>(l&63)<<(l&63)),
	b1.a[l>>6]&=~((~0ull)>>(63^(((r-l)&63)))<<(l&63)),void();
	b2.a[l>>6]|=(b1.a[l>>6]>>(l&63))<<(l&63),b2.a[r>>6]|=(b1.a[r>>6]<<(63^(r&63)))>>(63^(r&63));
	b1.a[l>>6]&=~((~0ull)<<(l&63));b1.a[r>>6]&=~((~0ull)>>(63^(r&63)));
	for(int i=(l>>6)+1;i<(r>>6);++i)b2.a[i]|=b1.a[i],b1.a[i]=0;
}
inline void work2(int now,int x,int y,int l,int r)noexcept{
	if(ans[now]==(x!=y))return;
	uint8_t*ax=(uint8_t*)(b[x].a),*ay=(uint8_t*)(b[y].a);
	const uint8_t alx=ax[l>>3],arx=ax[r>>3];
	const uint8_t aly=ay[l>>3],ary=ay[r>>3];
	ax[l>>3]=(ax[l>>3]>>(l&7))<<(l&7),ax[r>>3]=((uint8_t)(ax[r>>3]<<(7^(r&7))))>>(7^(r&7));
	ay[l>>3]=(ay[l>>3]>>(l&7))<<(l&7),ay[r>>3]=((uint8_t)(ay[r>>3]<<(7^(r&7))))>>(7^(r&7));
	int rxn=0,ryn=0;
	for(int i=(l>>3);i<=(r>>3);++i){
		ay[i]?(ans[now]=min(ans[now],rx[now]+calcl[ay[i]]),ryn=calcr[ay[i]]):(ryn=ry[now]+8),
		ax[i]?(ans[now]=min(ans[now],ry[now]+calcl[ax[i]]),rxn=calcr[ax[i]]):(rxn=rx[now]+8), 
		rx[now]=rxn,ry[now]=ryn,ans[now]=min(ans[now],t[ax[i]][ay[i]]);
	}
	ax[l>>3]=alx,ax[r>>3]=arx;
	ay[l>>3]=aly,ay[r>>3]=ary;
}
bool f=false;
inline void work(int L,int R){
	if(L>R)return;
	if(f)for(int i=1;i<N;++i)b[i].reset();f=true;
	for(int i=L;i<=R;++i)b[a[i]].flip(i-L);
	for(int i=1;i<=m;++i){
		int op=q[i].op,l=max(q[i].l,L)-L,r=min(q[i].r,R)-L,x=q[i].x,y=q[i].y;
		if(r<0||l>R-L)continue;
		if(op==1){if(x!=y)work1(b[x],b[y],l,r);}
		else work2(i,x,y,l,r);
	}
}
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	for(uint8_t i=255;i;--i)calcl[i]=__builtin_ctz(i),calcr[i]=8-__lg(i);
	for(int i=255;i>=0;--i){
		for(int j=255;j>=0;--j){
			int ans=0x3f3f3f3f;
			for(int ii=0;ii<8;++ii){
				for(int jj=0;jj<8;++jj){
					if(((i>>ii)&1)&&((j>>jj)&1))ans=min(ans,abs(ii-jj));
				}
			}
			t[i][j]=ans;
		}
	}cin>>n>>m;
	for(int i=1;i<=n;++i)cin>>a[i];
	for(int i=1;i<=m;++i)cin>>q[i].op>>q[i].l>>q[i].r>>q[i].x>>q[i].y,ans[i]=rx[i]=ry[i]=n;
	const int l1=n/4/64*64,l2=n*2/4/64*64,l3=n*3/4/64*64;
	work(1,l1);work(l1+1,l2);work(l2+1,l3);work(l3+1,n);
	for(int i=1;i<=m;++i)if(q[i].op==2)cout<<((ans[i]==n)?(-1):ans[i])<<'\n';
	return 0;
}

后文

posted @ 2026-09-09 12:37  kinggojianofyue  阅读(10)  评论(1)    收藏  举报