解题报告-和哈希构造近似充要条件

和哈希

有一种哈希方式叫做和哈希,其本质思想是将一个必要不充分条件近似转换成充要条件,并利用此条件进行答案的求解。

例 1. 杂题

题意:给定一个序列与 Q 次询问,每次判定一个区间 [l,r] 内是否存在出现次数为奇数的数字。

记命题 “区间 [l,r] 中存在出现次数为奇数的数字” 为 Q

思路:考虑异或的性质:奇数个 a 异或和 =a,偶数个 a 异或和 =0

所以 Q 成立的必要条件之一是“ [l,r] 内所有数的异或和不为 0 ”。

我们可以近似的认为,若 “该区间内所有数字的异或和为 0”,则 Q 成立。

但如果单纯这么算可能会被卡。

考虑对于每种数字赋一个随机权值,并以此随机权值进行异或计算。

可以发现此时出现错误判断的概率极低。

并且区间异或和可以方便的使用前缀异或和维护。

复杂度 O(n+q)

例 2. 相似序列

定义两个序列 A,B 相似,当且仅当 A,B 长度相同并且排序后 A,B 序列中至多有一个位置的数字不同。

给定一个序列与 Q 次询问,每次询问形如 l1,r1,l2,r2,求 [l1,r1] 构成的序列是否与 [l2,r2] 相似。

保证两段区间长度相等。

n,q,max{ai}105.

以下记 [l1,r1]=A,[l2,r2]=B

定义不和谐度为序列 A,B 分别排序后两者对应位置数字不同的位置个数。

算法一(n,q1000

暴力排序判断。

O(qnlogn)

算法二(ai100

注意到一个性质:

  • 如果两个区间的所有数字完全相同,则一定相似。

  • 如果两个区间有且仅有一个位置不同,则设 A 中此位置的数字在 A 去重后排名第 k 小,则 B 中此位置的数字在 A 去重后排名第 k+1k1。通俗的说:设 A 中此位置数字为 xB 中此位置数字为 y,则 A 中去重后 xy 中间没有数字。

证明:

第一条显然,这里证明第二条。

反证法,假设数字为 x,y,则作图:

则如果 YB 中变成 X

发现此时 [l,r] 整体右移一位,所以不和谐度至少增加 1

而此时 Y 已经对不和谐度产生了 1 的贡献,所以修改后一定不合法。

证毕。

综上,可以开 100 个桶来求解区间某个数字的个数,分别找出最小的、最大的区间个数不相同的数字,再判断区间内是否存在在他们值域之间的数字。

复杂度 O(qV)V 为值域。

const int N=1e5+10;
int cnt[110][N];
int n,q;
main() { 
	cin>>n>>q;
	FOR(i,1,n) {
		int x=read();
		FOR(j,1,100) cnt[j][i]=cnt[j][i-1];
		++cnt[x][i];
	}
	while(q--) {
		int l1=read(),r1=read(),l2=read(),r2=read();
		int mn=1e9,mx=0;
		FOR(i,1,100) if(cnt[i][r1]-cnt[i][l1-1]!=cnt[i][r2]-cnt[i][l2-1]) cmin(mn,i),cmax(mx,i);
		if(!mx) {
			puts("YES");
			continue;
		}
		int pk=0;
		FOR(i,mn+1,mx-1) {
			if(max(cnt[i][r1]-cnt[i][l1-1],cnt[i][r2]-cnt[i][l2-1])) {
				puts("NO");
				pk=1;break;
			}
		}
		if(pk) continue;
		if(abs((cnt[mx][r1]-cnt[mx][l1-1])-(cnt[mx][r2]-cnt[mx][l2-1]))>1) puts("NO");
		else puts("YES");
	} 
	return 0;
}

算法三(正解)

注意到这玩意本质上是判断桶是否相同。

可以使用主席树维护桶。

那么找最大/最小的桶内不相同的值可以使用线段树上二分或者二分+线段树。

至于如何判断值域 1V 是否相同,使用和哈希,对值域每个数字单独随机一个权值,这样主席树只需要维护区间和,并可以方便的使用作差维护两个版本作差后的主席树。

O(nlogn)

const int N=1e5+10,P=131;
int n,q,root[N],tot,num[N];
ULL p[N];
mt19937 rnd(time(0));
struct Node {int ls,rs;ULL H,siz;}t[N*40];
int Build(int nd) {return t[++tot]=t[nd],tot;}
int Insert(int p,int l,int r,int x) {
	p=Build(p);t[p].H+=num[x],t[p].siz++;
	if(l==r) return p;int mid=l+r>>1;
	x<=mid?t[p].ls=Insert(t[p].ls,l,mid,x):t[p].rs=Insert(t[p].rs,mid+1,r,x);return p;
}
ULL Ask(int p,int q,int ql,int qr,int l,int r) {
	if(ql>qr) return 0; 
	if(ql<=l&&r<=qr) return t[p].H-t[q].H;
	int mid=l+r>>1;ULL S=0;
	if(ql<=mid) S+=Ask(t[p].ls,t[q].ls,ql,qr,l,mid);
	if(qr>mid) S+=Ask(t[p].rs,t[q].rs,ql,qr,mid+1,r);
	return S;
}
int Cnt(int p,int q,int l,int r,int x) {
	if(l==r) return t[p].siz-t[q].siz;
	int mid=l+r>>1;return x<=mid?Cnt(t[p].ls,t[q].ls,l,mid,x):Cnt(t[p].rs,t[q].rs,mid+1,r,x);
}
main() { 
	cin>>n>>q;
	int V=1e5;FOR(i,1,V) num[i]=rnd()%1000000000;
	p[0]=1;FOR(i,1,V) p[i]=p[i-1]*P;
	FOR(i,1,n) root[i]=Insert(root[i-1],1,V,read());
	while(q--) {
		int l1=read(),r1=read(),l2=read(),r2=read();
		if(Ask(root[r1],root[l1-1],1,V,1,V)==Ask(root[r2],root[l2-1],1,V,1,V)) {puts("YES");continue;}
		int l=1,r=V,mid,posl=0;
		while(mid=l+r>>1,l<=r) Ask(root[r1],root[l1-1],1,mid,1,V)!=Ask(root[r2],root[l2-1],1,mid,1,V)?posl=mid,r=mid-1:l=mid+1;
		l=1,r=V;int posr=V+1;
		while(mid=l+r>>1,l<=r) Ask(root[r1],root[l1-1],mid,V,1,V)!=Ask(root[r2],root[l2-1],mid,V,1,V)?posr=mid,l=mid+1:r=mid-1;
		if(!Ask(root[r1],root[l1-1],posl+1,posr-1,1,V)&&!Ask(root[r2],root[l2-1],posl+1,posr-1,1,V)&&abs(Cnt(root[r1],root[l1-1],1,V,posl)-Cnt(root[r2],root[l2-1],1,V,posl))<=1) puts("YES");
		else puts("NO");
	}
	return 0;
}
posted @   cannotmath  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示