题解 P5065 【[Ynoi2014]不归之人与望眼欲穿的人们】

时间:2021-03-08 05:05:46

出现了一篇跑得炒鸡慢的题解!

noteskey

无 fuck 说,好像就是整个数列分块然后合并区间...什么的吧

对于每块内部就是算一下前缀信息、后缀信息(就是以 第一个点/最后一个点 为一个边界,不超过 log 个不同的 or 值所要到达的最 左/右 点)和中值信息(就是某种区间长度内能 or 出来的最大值)

然后询问的时候从第一个块开始,向后先查询当前块与下一个块合并的答案,然后更新当前块

watch out

nothing ,打代码的时候注意细节貌似是所有 coding 的 gift ? 【雾

code

//by Judge
#pragma GCC optimize(3)
#pragma GCC optimize("unroll-loops")
#include<bits/stdc++.h>
#define Rg register
#define fp(i,a,b) for(Rg int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(Rg int i=(a),I=(b)-1;i>I;--i)
#define P pair<int,int>
#define ll long long
#define se second
#define fi first
using namespace std;
const int bl=403;
const int M=6e4+3;
typedef int arr[M];
#ifndef Judge
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#endif
char buf[1<<21],*p1=buf,*p2=buf;
bool operator <(P& a,P& b){return a.fi^b.fi?a.fi<b.fi:a.se>b.se;}
inline bool cmax(int& a,int b){return a<b?a=b,1:0;}
inline bool cmin(int& a,int b){return a>b?a=b,1:0;}
inline int read(){ int x=0,f=1; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
} char sr[1<<21],z[20];int CCF=-1,Z;
inline void Ot(){fwrite(sr,1,CCF+1,stdout),CCF=-1;}
inline void print(int x,char chr='\n'){
if(CCF>1<<20)Ot();if(x<0)sr[++CCF]=45,x=-x;
while(z[++Z]=x%10+48,x/=10);
while(sr[++CCF]=z[Z],--Z);sr[++CCF]=chr;
} int n,m,K,ans,pv,a[M],pos[M];
P q[M],vec[bl+3],c[bl+3];
struct BLK{ int mx[bl+3],val[bl+3],L,R,cntp,cnts,all; P pre[33],suf[33]; //最多 log 个不同的 or 值
// mx[i] 长为 i 的区间 or 和的 max 值,val 表示块内每个点的值
inline void re(){ memset(mx,0,sizeof mx); int p; // 就算 mx 数组
p=cntp=0; fp(i,1,bl) if((p|=val[i])!=pre[cntp].fi) pre[++cntp]=P(p,i+L-1);
p=cnts=0; fd(i,bl,1) if((p|=val[i])!=suf[cnts].fi) suf[++cnts]=P(p,i+L-1);
int head=1,tail=0;
fd(i,bl,1){ int lst=tail,v=val[i];
for(Rg int j=v;j;j^=j&-j) q[++tail]=P(__builtin_ctz(j),i);
fp(j,head,lst) if(!(v>>q[j].fi&1)) q[++tail]=q[j]; head=lst+1;
Rg int now=0,pos; fp(j,head,tail)
pos=q[j].se,cmax(mx[pos-i+1],now|=val[pos]);
}
fp(i,2,bl) cmax(mx[i],mx[i-1]); all=pre[cntp].fi;
}
inline void build(int l,int r){ L=l,R=r;
fp(i,l,r) val[i-l+1]=a[i]; re();
}
inline void update(int pos,int v){val[pos-L+1]=v,re();} //重构块
inline int len(int v){return lower_bound(mx+1,mx+1+bl,v)-mx;}
//得到构成长度为 v 的 or 和区间最短长度
inline void merge(){ int l=1,r=1,it=0; //将当前的区间和 vec 后缀合并
while(l<=pv&&r<=cnts) c[++it]=vec[l]<suf[r]?vec[l++]:suf[r++];
while(l<=pv) c[++it]=vec[l++]; while(r<=cnts) c[++it]=suf[r++];
vec[1]=c[1]; fp(i,2,it) vec[i]=c[i],
vec[i].fi==vec[i-1].fi&&(vec[i].se=vec[i-1].se);
pv=unique(vec+1,vec+1+it)-1-vec;
}
inline void check(int v){ int len=2e9; //用后缀 vec 和当前块的前缀更新答案
fp(i,1,cntp) while(pv&&(vec[pv].fi|pre[i].fi)>=v)
cmin(len,pre[i].se-vec[pv--].se+1); cmin(ans,len);
}
}b[131];
int main(){ n=read(),m=read(); fp(i,1,n) a[i]=read();
fp(i,1,n) pos[i]=(i-1)/bl+1; K=pos[n],n=K*bl;
fp(i,1,K) b[i].build((i-1)*bl+1,i*bl);
while(m--){
if(read()&1){ int x=read(),y=read();
b[pos[x]].update(x,y),a[x]=y;
} else{ ans=2e9; int x=read();
fp(i,1,K) if(b[i].all>=x)
cmin(ans,b[i].len(x));
pv=0,b[1].merge();
fp(i,2,K){
b[i].check(x);
fp(j,1,pv) vec[j].first|=b[i].all;
b[i].merge();
} print(ans<2e9?ans:-1);
}
} return Ot(),0;
}