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;
}
静渊以有谋,疏通而知事。