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

· · 算法·理论

这是上一篇。

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

1

题目

qoj#1851. Directed Acyclic Graph

题面

::::info[做法] lxl 说这题比追忆难。 这种 DAG 可达性基本上都可以这样操作分块:每 $w(w=64)$ 次操作为一组,复杂度做到 $O(\frac{q(n+m)}{w})$。好处是空间很小,且只需要用单个 `unisnged long long` 维护一个点的可达性,更好写;坏处是这个常数往往会大一些。 本题也是如此:每 $w$ 此操作为一组,分别维护最小值赋值以及所有赋值的可达性。注意一下细节:例如最小值赋值的可达性需将所有值排序。 代码: ```cpp #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] 由乃的玉米田](https://www.luogu.com.cn/problem/P5355) ### 题面 长度为 $n$ 的序列 $a$,有 $m$ 次操作,每次询问区间内是否存在两个数使得和、差、积或商为 $x$(128MB,本处要求强制在线)。 ::::info[做法] 加减乘可按前面的思想块间建立 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 空间在线地通过了本题: ```cpp 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](https://www.luogu.com.cn/problem/P5313) ### 题面 长度为 $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)。 ::::info[做法] 考虑莫队加上 `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` 写,其实是他可能写得劣一些。接下来,给大家说一种比较好写的方法。 首先是一些基础的: ```cpp 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 还有右端点的取值。 ```cpp 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}$,要不然复杂度就假了。 总的代码: ```cpp #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] 未来日记](https://www.luogu.com.cn/problem/P4119) ### 题面 长度为 $n$ 的序列 $a$,$m$ 次操作:每次将 $[l,r]$ 值为 $x$ 的数变为 $y$ 或查询 $[l,r]$ 中的第 $k$ 小值(512MB)。 ::::info[做法] 考虑对于每个数维护一个位置的 `bitset`,每 $C$ 个数维护一个位置的 `bitset`,如果不看空间可以手写 `bitset` 维护前缀 $1$ 的个数,但是本题空间稍微有点紧。 不妨在每个数存在的位置个数小于等于 $B$ 时用 `vector` 存,反之用 `bitset` 存。$B$ 与 $C$ 都取 $\sqrt n$ 左右即可通过。 我相信大家都会写 `bitset`: ```cpp #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] 手牵手走向明天](https://www.luogu.com.cn/problem/P5692) ### 题面 长度为 $n$ 的序列 $a$,$m$ 次操作:每次将 $[l,r]$ 值为 $x$ 的数变为 $y$ 或查询 $[l,r]$ 中值为 $x$ 的位置与值为 $y$ 的位置的差的绝对值的最小值,无解输出 $-1$(1.5s)。 ::::info[做法] 喜欢出单根 ds 题不带权的出题人们你们好呀,我是 `bitset`。 跟上一题类似,依旧每个数用 `bitset` 维护其出现的位置。依旧空间不够,把 $1$ 个序列拆成 $4$ 个即可。 查询似乎不太好做,不妨更暴力一些,直接预处理 $256\times256$ 的表作为内部的贡献,外面的贡献位运算即可,复杂度是 $O(\frac{n^2}{\sqrt w})$ 的,加点剪枝即可通过。 代码: ```cpp #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; } ``` :::: ## 后文 ![](https://cdn.luogu.com.cn/upload/image_hosting/sdzl8usk.png)