[Ynoi2014]不归之人与望眼欲穿的人们
Posted mrsrz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Ynoi2014]不归之人与望眼欲穿的人们相关的知识,希望对你有一定的参考价值。
题目大意:
给定一个序列,每次单点修改一个数,或给定$x$,询问最短的or起来大于等于$x$的区间的长度(不存在输出-1)。
解题思路:
在太阳西斜的这个世界里,置身天上之森。等这场战争结束之后,不归之人与望眼欲穿的众人, 人人本着正义之名,长存不灭的过去、逐渐消逝的未来。我回来了,纵使日薄西山,即便看不到未来,此时此刻的光辉,盼君勿忘。————世界上最幸福的女孩
盼你归来,珂朵莉~
---
这些题目果然一道比一道神仙
众所周知,这道题分块。
然后你就珂以去看shadowice1984的题解了
好吧窝来复述一下吧(雾)
首先有一个结论,对于一个区间,如果其中一个端点固定,则区间or和的不同取值最多$log a$个,因为or和是按位变大的(不会变小)。
先假设我们手里有一个已经分好块的序列,块大小为$S$,那么对于询问,就有两种情况:区间在一个块内,区间跨了几个块。
考虑处理出每个块内,长度为$len$的区间的最大异或和。然后对于区间在块内的情况,直接二分即可。单次询问复杂度$O(frac{nlog S}{S})$。
由于有单点修改操作,我们还要考虑快速修改这个数组。根据刚刚说的结论,对每个点作端点,不同or和最多$log a$个,所以处理出每个数的每一位在之后(包括自己)第一次出现的位置,然后按顺序or即可。
还有个问题,我们需要“按顺序”or,排序的话就会多一个$loglog a$。
假设我们已经知道位置$i$要or的那些位置的顺序构成的队列,对于位置$i-1$,先把它出现过的位塞进队列,再从前往后扫描位置$i$的队列,把没出现在位置$i-1$的位塞进队列。这个队列就是第$i-1$位要转移的位置的顺序。单次复杂度$O(Slog a)$。
现在考虑跨块的区间。我们从左往右枚举块,处理以第$i$个块的块尾结尾的后缀的不同or和(同时记录位置)。
然后考虑第$i+1$个块的所有不同前缀,用双指针扫描后缀和前缀(左端点往右,右端点也往右),更新答案即可。
接着考虑如何从第$i$块的后缀转移到第$i+1$块的贡献。先把原来的后缀or上第$i+1$块所有数的or和,然后用归并排序把第$i+1$块的后缀并上去即可。最后去重,出现多次的保留最后出现的即可。
复杂度$O(frac{nlog a}{S})$。
$S$取$sqrt n$,得总复杂度$O(nsqrt nlog n)$。
然后卡卡常数。
C++ Code:
#pragma GCC optimize("Ofast") #pragma GCC optimize("unroll-loops") #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native") #include<cstdio> #include<cctype> #include<utility> #include<algorithm> #include<cstring> char buf[8888888],*it; void init(){fread(it=buf,1,8888886,stdin);fclose(stdin);} inline int readint(){ int d=0; for(;!isdigit(*it);++it); while(isdigit(*it))d=d*10+(*it++^‘0‘);return d; } #define mp std::make_pair #define bel(x)((x-1)/siz+1) typedef std::pair<int,int>PII; inline bool operator<(const PII&a,const PII&b){ return(a.first!=b.first)?a.first<b.first:a.second>b.second; } const int siz=400; int a[60000]; inline void chkmax(int&a,const int&b){if(a<b)a=b;} inline void chkmin(int&a,const int&b){if(a>b)a=b;} int ans,pv; PII q[23333],vec[66],c[66]; struct BLOCK{ int mx[402],val[402],L,R,cntp,cnts,all; PII pre[33],suf[33]; void re(){ memset(mx,0,sizeof mx); for(int i=1,p=cntp=0;i<=siz;++i) if((p|=val[i])!=pre[cntp].first)pre[++cntp]=mp(p,i+L-1); for(int i=siz,p=cnts=0;i;--i) if((p|=val[i])!=suf[cnts].first)suf[++cnts]=mp(p,i+L-1); int head=1,tail=0; for(int i=siz;i;--i){ const int lst=tail,v=val[i]; for(int j=v;j;j^=j&-j) q[++tail]=mp(__builtin_ctz(j),i); for(int j=head;j<=lst;++j) if(!(v>>q[j].first&1))q[++tail]=q[j]; head=lst+1; for(int j=head,now=0;j<=tail;++j){ const int pos=q[j].second; chkmax(mx[pos-i+1],now|=val[pos]); } } for(int i=2;i<=siz;++i)chkmax(mx[i],mx[i-1]); all=pre[cntp].first; } void build(int l,int r){ L=l,R=r; for(int i=l;i<=r;++i)val[i-l+1]=a[i]; re(); } void modify(int pos,int v){val[pos-L+1]=v;re();} inline int getlen(int v){return std::lower_bound(mx+1,mx+siz+1,v)-mx;} inline void merge_suffix_to_vec(){ int l=1,r=1,it=1; 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]; for(int i=2;i<it;++i){ vec[i]=c[i]; if(vec[i].first==vec[i-1].first)vec[i].second=vec[i-1].second; } pv=std::unique(vec+1,vec+it)-vec-1; } inline void check_prefix_with_vec(const int&v){ int len=233333; for(int i=1;i<=cntp;++i) while(pv&&(vec[pv].first|pre[i].first)>=v) chkmin(len,pre[i].second-vec[pv--].second+1); chkmin(ans,len); } }b[130]; int n,m,K; int main(){ init(); n=readint(),m=readint(); for(int i=1;i<=n;++i)a[i]=readint(); n=bel(n)*siz; K=n/siz; for(int i=1;i<=K;++i)b[i].build((i-1)*siz+1,i*siz); while(m--) if(readint()==1){ const int pos=readint(),x=readint(); b[bel(pos)].modify(pos,x); a[pos]=x; }else{ ans=233333; const int x=readint(); for(int i=1;i<=K;++i) if(b[i].all>=x)chkmin(ans,b[i].getlen(x)); pv=0; b[1].merge_suffix_to_vec(); for(int i=2;i<=K;++i){ b[i].check_prefix_with_vec(x); for(int j=1;j<=pv;++j)vec[j].first|=b[i].all; b[i].merge_suffix_to_vec(); } printf("%d ",(ans==233333)?-1:ans); } return 0; }
以上是关于[Ynoi2014]不归之人与望眼欲穿的人们的主要内容,如果未能解决你的问题,请参考以下文章