给个链接:集合。
很神秘的题目。基本上看到之后就可以想到哈希。
首先想到一个比较神秘的暴力。就是对于每个询问,扫一遍所有\(a\) 中的数出现的位置,把它弄成一个哈希值(具体怎么弄随意)存到 set
里,然后看看是不是和 \(b\) 中的数出现的位置这样操作后的集合完全相等。事实上就是判断是否对于所有在 \(a\) 中这个区间内出现的数 \(x\),都存在一个在 \(b\) 中出现的不同的数 \(y\),使得 \(x,y\) 出现的位置完全相同。这样做应该是有 \(70\) 分的,但是显然不够。
然后我们考虑一个事情,对于每一个 \(i\),\([i,j]\) 这个区间合法在 \(j\) 尽可能小的时候最有可能成立。换句话说有单调性,可以二分。
但是,二分是没有必要的,我们可以用双指针做的更好。因为,如果 \([i,j]\) 合法,\([i+1,j]\) 存在,那么 \([i+1,j]\) 合法。
所以我们对每个 \(i\) 找出最靠右的 \(j\) 且满足 \([i,j]\) 合法,然后就可以 \(O(1)\) 回答询问。时间复杂度 \(O(n+q)\)。
这里为了保险,把每个位置的值也做了哈希,用 \(p_i\) 存储。
给个代码:
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define N 200005
using namespace std;
int n,m,q,a[N][3],b[N][3];
ull nowa[N],nowb[N],suma,sumb,p[N];
int ri[N];//使[i,j]合法的最靠右的j
ull get_rnd(ull x){
return x*x*x;//随便变换一下
}
void work(int id,int type){
for(int j=0;j<3;j++){
ull i=a[id][j];
suma-=get_rnd(nowa[i]);//把原来的值减掉
nowa[i]+=type*p[id];//看情况加上或减去这个位置的哈希值
suma+=get_rnd(nowa[i]);//加上现在的值
}
for(int j=0;j<3;j++){//这里同理
ull i=b[id][j];
sumb-=get_rnd(nowb[i]);
nowb[i]+=type*p[id];
sumb+=get_rnd(nowb[i]);
}
}
signed main(){
srand(time(0));
cin>>n>>m>>q;
for(int i=1;i<=n;i++){
for(int j=0;j<3;j++){
cin>>a[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<3;j++){
cin>>b[i][j];
}
}
for(int i=1;i<=n;i++){
ri[i]=n;
}
for(int i=1;i<=n;i++){
p[i]=rand()*rand()+923;
}
work(1,1);
for(int i=1,j=1;i<=n;i++){
while(j<=n&&suma==sumb){//如果当前仍然合法
j++;
if(j>n)break;
work(j,1);
}
ri[i]=min(ri[i],j-1);
work(i,-1);//i要右移,所以撤掉这一位的贡献
}
while(q--){
int l,r;
cin>>l>>r;
cout<<(ri[l]>=r?"Yes\n":"No\n");
}
return 0;
}
标签:哈希,int,题解,long,合法,NOI2024,ull,集合,ri
From: https://www.cnblogs.com/zxh923aoao/p/18314343