浅谈 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
题目
题面
长度为 \(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
题目
题面
长度为 \(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
题目
题面
长度为 \(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;
}
后文


浙公网安备 33010602011771号