[Ynoi2014]不归之人与望眼欲穿的人们

题目大意:

给定一个序列,每次单点修改一个数,或给定$x$,询问最短的or起来大于等于$x$的区间的长度(不存在输出-1)。

解题思路:

在太阳西斜的这个世界里,置身天上之森。等这场战争结束之后,不归之人与望眼欲穿的众人, 人人本着正义之名,长存不灭的过去、逐渐消逝的未来。我回来了,纵使日薄西山,即便看不到未来,此时此刻的光辉,盼君勿忘。————世界上最幸福的女孩

盼你归来,珂朵莉~

---

这些题目果然一道比一道神仙

众所周知,这道题分块。

然后你就珂以去看shadowice1984的题解了

好吧窝来复述一下吧(雾)

首先有一个结论,对于一个区间,如果其中一个端点固定,则区间or和的不同取值最多$\log a$个,因为or和是按位变大的(不会变小)。

先假设我们手里有一个已经分好块的序列,块大小为$S$,那么对于询问,就有两种情况:区间在一个块内,区间跨了几个块。

考虑处理出每个块内,长度为$len$的区间的最大异或和。然后对于区间在块内的情况,直接二分即可。单次询问复杂度$O(\frac{n\log S}{S})$。

由于有单点修改操作,我们还要考虑快速修改这个数组。根据刚刚说的结论,对每个点作端点,不同or和最多$\log a$个,所以处理出每个数的每一位在之后(包括自己)第一次出现的位置,然后按顺序or即可。

还有个问题,我们需要“按顺序”or,排序的话就会多一个$\log\log a$。

假设我们已经知道位置$i$要or的那些位置的顺序构成的队列,对于位置$i-1$,先把它出现过的位塞进队列,再从前往后扫描位置$i$的队列,把没出现在位置$i-1$的位塞进队列。这个队列就是第$i-1$位要转移的位置的顺序。单次复杂度$O(S\log a)$。

现在考虑跨块的区间。我们从左往右枚举块,处理以第$i$个块的块尾结尾的后缀的不同or和(同时记录位置)。

然后考虑第$i+1$个块的所有不同前缀,用双指针扫描后缀和前缀(左端点往右,右端点也往右),更新答案即可。

接着考虑如何从第$i$块的后缀转移到第$i+1$块的贡献。先把原来的后缀or上第$i+1$块所有数的or和,然后用归并排序把第$i+1$块的后缀并上去即可。最后去重,出现多次的保留最后出现的即可。

复杂度$O(\frac{n\log a}{S})$。

$S$取$\sqrt n$,得总复杂度$O(n\sqrt n\log 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\n",(ans==233333)?-1:ans);
	}
	return 0;
}

 

posted @ 2019-02-17 17:41  Mrsrz  阅读(862)  评论(0编辑  收藏  举报