LOJ6169 相似序列

相似序列

给定长度为 \(n\) 的整数序列 \(A\),你需要回答 \(q\) 个询问。询问给定两个子串 \([a, b]\)\([c, d]\),要求你判断两个子串是否相似。

如果两个序列在各自排序后,除最多一个位置外,其他所有位置上的元素对应相等,那么我们认为两个序列相似。

请注意,给定的子串可以有公共部分,但不会互相影响。

\(1 \leq n,q \leq 10 ^ 5\)

题解

http://jklover.hs-blog.cf/2020/05/28/Loj-6169-相似序列/#more

Hash + 主席树.

给每种元素随机分配一个大权值,若两个区间内权值和相等,就可以认为它们在排序后是一样的.

现在可以允许排序后有一个位置不同.

以权值为下标建出主席树,二分出第一个不能匹配的权值 \(x\) ,最后一个不能被匹配的权值 \(y\) .

\(x,y\) 是来自两个不同的子串,且 \([x,y]\) 内没有其它权值,说明排序后它们位置能对应上,两个子串就相似了.

可以记录一下每种权值的个数,方便判断.

时间复杂度 \(O(n\log n)\) .

IN uint64 gen(){
	uint64 a=rand(),b=rand(),c=rand(),d=rand();
	return a|b<<15|c<<30|d<<45;
}

CO int N=1e5+10;
int a[N];
uint64 val[N],b[N];
int root[N],tot;
int lc[N*20],rc[N*20],cnt[N*20];
uint64 sum[N*20];

#define mid ((l+r)>>1)
void insert(int&x,int l,int r,int p,uint64 v){
	++tot,lc[tot]=lc[x],rc[tot]=rc[x];
	cnt[tot]=cnt[x]+1,sum[tot]=sum[x]+v,x=tot;
	if(l==r) return;
	if(p<=mid) insert(lc[x],l,mid,p,v);
	else insert(rc[x],mid+1,r,p,v);
}
pair<int,int> query_first(int A,int B,int C,int D,int l,int r){
	if(l==r){
		if(sum[B]-sum[A]==sum[D]-sum[C]) return {-1,0};
		return {l,cnt[B]-cnt[A]-(cnt[D]-cnt[C])};
	}
	if(sum[lc[B]]-sum[lc[A]]!=sum[lc[D]]-sum[lc[C]])
		return query_first(lc[A],lc[B],lc[C],lc[D],l,mid);
	else
		return query_first(rc[A],rc[B],rc[C],rc[D],mid+1,r);
}
pair<int,int> query_last(int A,int B,int C,int D,int l,int r){
	if(l==r){
		if(sum[B]-sum[A]==sum[D]-sum[C]) return {-1,0};
		return {l,cnt[B]-cnt[A]-(cnt[D]-cnt[C])};
	}
	if(sum[rc[B]]-sum[rc[A]]!=sum[rc[D]]-sum[rc[C]])
		return query_last(rc[A],rc[B],rc[C],rc[D],mid+1,r);
	else
		return query_last(lc[A],lc[B],lc[C],lc[D],l,mid);
}
int query_cnt(int A,int B,int C,int D,int l,int r,int ql,int qr){
	if(ql<=l and r<=qr) return cnt[B]-cnt[A]+cnt[D]-cnt[C];
	if(qr<=mid)
		return query_cnt(lc[A],lc[B],lc[C],lc[D],l,mid,ql,qr);
	if(ql>mid)
		return query_cnt(rc[A],rc[B],rc[C],rc[D],mid+1,r,ql,qr);
	return query_cnt(lc[A],lc[B],lc[C],lc[D],l,mid,ql,qr)+query_cnt(rc[A],rc[B],rc[C],rc[D],mid+1,r,ql,qr);
}
bool check(int A,int B,int C,int D){
	if(B-A!=D-C) return 0;
	pair<int,int> x=query_first(root[A-1],root[B],root[C-1],root[D],1,1e5);
	if(x.first==-1) return 1;
	pair<int,int> y=query_last(root[A-1],root[B],root[C-1],root[D],1,1e5);
	if(x.first==y.first) return 1;
	if((x.second>0)==(y.second>0)) return 0;
	if(abs(x.second)>1 or abs(y.second)>1) return 0;
	if(x.first+1>y.first-1) return 1;
	return query_cnt(root[A-1],root[B],root[C-1],root[D],1,1e5,x.first+1,y.first-1)==0;
}
#undef mid

void real_main(){
	tot=0;
	for(int i=1;i<=1e5;++i) val[i]=gen();
	int n=read<int>(),q=read<int>();
	for(int i=1;i<=n;++i){
		read(a[i]),b[i]=val[a[i]];
		insert(root[i]=root[i-1],1,1e5,a[i],b[i]);
	}
	while(q--){
		int A=read<int>(),B=read<int>(),C=read<int>(),D=read<int>();
		puts(check(A,B,C,D)?"YES":"NO");
	}
}
int main(){
	srand(20030506);
	for(int T=read<int>();T--;) real_main();
	return 0;
}

posted on 2020-06-03 20:13  autoint  阅读(201)  评论(0编辑  收藏  举报

导航