首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

关于异或哈希

编程知识
2024年09月24日 10:58

Re:疑惑异或哈希

异或哈希是个很神奇的算法,利用了异或操作的特殊性和哈希降低冲突的原理,可以用于快速找到一个组合是否出现、序列中的数是否出现了 \(k\)

算法如其名,异或+哈希。

想起某首歌叫PPAP

I have a \(\oplus\),I have an \(hash\).
(Uhh~) \(\oplus hash\) !

😅

异或

最基本的,也很重要的

\[a\oplus 0 =a \]

\[a\oplus a =0 \]

考虑到异或运算满足交换律,即\(a \oplus b=b\oplus a\)

因此组合的不同排列的异或值相等

\[\begin{align*} a \oplus b \oplus c&= a \oplus c \oplus b\\ &= b \oplus a \oplus c\\ &= b \oplus c \oplus a\\ &= c \oplus a \oplus b\\ &= c \oplus b \oplus a\\ \end{align*} \]

这样我们便可以忽略顺序,直接统计组合出现次数。

哈希

由于二进制位数有限,会存在一些情况使得异或并不正确,如:\(1\oplus 2=5\oplus 6\)\(1\oplus 2\oplus 3 = 4\oplus 8\oplus 12\)

因此需要将原始数据哈希为较大的数降低冲突概率

应用

组合问题

直接来看道例题。

here

不会有人看不懂英文还不会拿软件翻译吧 虽然翻译软件翻译的跟构式一样

直接沿用异或哈希的思路,不难写出 \(O(n^2)\) 的代码

int n,m;

mt19937_64 rnd(time(0));

unsigned long long code[N],chk[N],Xor[N],a[N];

signed main(){
	n=rd;
	for(int i=1;i<=n;i++){
		a[i]=rd;
	}
	
	for(int i=1;i<=n;i++){
		code[i]=rnd();
		chk[i]=chk[i-1]^code[i];
	}
	
	for(int i=1;i<=n;i++){
		Xor[i]=Xor[i-1]^code[a[i]];
	}
	
	int cnt=0;
	for(int i=1;i<=n;i++){
		for(int j=i;j<=n;j++){
			if((Xor[i-1]^Xor[j])==chk[j-i+1]){
				++cnt;
			}
		}
	}
	
	printf("%lld",cnt);
	return Elaina;
}

兴高采烈地交上去...乂~T了。

一看数据范围:\(3*10^5\)

好家伙 \(O(n^2)\) 肯定 过不去了。

进一步考虑到:

  • 满足条件的区间肯定有 \(1\)等于区间长度的最大值 \(max\)

分别向右/左做两次扩展:

\(pos\) 记录上一个 \(1\) 的位置,当前扩展到 \(x\)

\(x=1\) 时,就更新指针 \(pos\),并重置 \(mx\)

否则往回捯饬 \(mx=max(mx,x)\) 个数,异或哈希判断是否合法即可。

int n,m;

int max(int a,int b){
	return a>b?a:b;
}

mt19937_64 rnd(time(0));

unsigned long long code[N],chk[N],Xor[N],a[N];

signed main(){
	n=rd;
	for(int i=1;i<=n;i++){
		a[i]=rd;
	}
	
	for(int i=1;i<=n;i++){
		code[i]=rnd();
		chk[i]=chk[i-1]^code[i];
	}
	
	for(int i=1;i<=n;i++){
		Xor[i]=Xor[i-1]^code[a[i]];
	}
	
	int cnt=0,pos=-1,cnt1=0,mx=0;
	for(int i=1;i<=n;i++){
		if(a[i]==1){
			pos=i,++cnt1,mx=1;
		}else if(pos!=-1){
			mx=max(mx,a[i]);
			if(i-mx+1<=pos)
				if((Xor[i]^Xor[i-mx])==chk[mx])
					++cnt;
		}
	}

	pos=-1;
	for(int i=n;i>=1;i--){
		if(a[i]==1){
			pos=i,++cnt1,mx=1;
		}else if(pos!=-1){
			mx=max(mx,a[i]);
			if(i+mx>=pos)
				if((Xor[i+mx-1]^Xor[i-1])==chk[mx])
					++cnt;
		}
	}
	
	printf("%lld",cnt+cnt1/2);
	return Elaina;
}

实际上反过来的时候可以直接reverse一下数组,然后再跑一遍,就不用复制粘贴打两遍了。。。

然后就 AC 了喵~

出现次数问题

二进制的异或的本质是对每一位进行不进位的加法,也就是每一位相加对2取模,即:

\[0 \oplus 0 = (0+0)\ \%\ 2 = 0 \]

\[0 \oplus 1 = (0+1)\ \%\ 2 = 1 \]

\[1 \oplus 0 = (1+0)\ \%\ 2 = 1 \]

\[1 \oplus 1 = (1+1)\ \%\ 2 = 0 \]

假设有一种运算 \(③\),使得 \(a\ ③\ a\ ③\ a=0\),即

\[0\ ③\ 0 = (0+0)\ \%\ 3 = 0 \]

\[1\ ③\ 0 = (0+1)\ \%\ 3 = 1 \]

\[2\ ③\ 0 = (2+0)\ \%\ 3 = 2 \]

\[2\ ③\ 2 = (2+2)\ \%\ 3 = 1 \]

其实也就是 \(3\) 进制的运算

扩展到 \(k\) 进制即可解决是否出现 \(k\) 次的问题

ull xork(ull a,ull b,int k){
	vector<int> vec;
	while(a||b){
	    vec.push_back((a+b)%k);
	    a/=k;
	    b/=k;
	}
	
	ull res=0;
	ull p=1;
	
	for(auto x:vec){
	    res+=p*x;
	    p*=k;
	}
	
	return res;
}

ull xork(ull x,ull y,int k){
	int sum=0,p=1;
//	if(y%2==0) swap(x,y);
	while(x||y){
		sum+=(x%k+y%k)%k*p;
		p*=k;
		x/=k;
		y/=k;
	}
	return sum;
}

我们还是来看道例题

here

洛谷

其实上一道题也可以在洛谷找到对应的翻译版本 (逃

首先我们知道一个思想,证明充要条件就要证明它既充分又必要;同样,要证明一个数等于某个值,必须让它既小于等于又大于等于这个值。
迁移到本题,我们让所有数的出现个数 \(cnt = 3\) ,便是要去满足 \(cnt \geq 3 \land cnt \leq 3\) 这俩约束

第一个约束随便糊一个异或哈希即可。

关键在于第二个约束。

我们考虑使用类似于双指针的算法:

考虑对于一个满足约束二的 \([l, r]\) 区间,右指针每次往右移动一次,都可能会破坏原本“满足约束二”的性质。那么为了让其重新满足,我们需要让左指针一直向右移动,即:从左到右删去数字使得区间再次满足约束二。

让新加入的右指针的值 \(a_r\) 出现的次数小于等于三即可;因为这样删除必然不会导致“因为其他数字出现次数减少而导致不能满足约束二”这种情况,理由显然。

\(pre_r\)\([1, r]\) 的前缀异或和。

当删除完毕之后,我们统计满足 \(pre_r = pre_{pos}\)\(pos \in [l, r]\)\(pos\) 数量,这一点可以使用 map 或者哈希表完成。

那么这道题就完成了,复杂度 \(\mathcal{O}(N \log_2 N)\) 或者纯线性。

然后关于...算了,自己看吧。

discuss

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define ull unsigned long long
#define rd read()
#define mkp make_pair
#define psb push_back
#define Elaina 0
inline ll read(){
	ll f=1,x=0;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) f=(ch=='-'?-1:1);
	for(;isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
	return f*x;
}
const int mod=1e9+7;
const int N=1e6+100;
const int inf=0x7fffffff;
 
int n,k;
 
mt19937_64 rnd(time(0));
 
ull code[N],pre[N],a[N],num[N];
map<ull,ull> mp; 
 
ull xork(ull x,ull y,int k){
	ull sum=0,p=1;
	while(x||y){
		sum+=(x%k+y%k)%k*p;
		p*=k;
		x/=k;
		y/=k;
	}
	return sum;
}

 
signed main(){
	srand(time(0));
	
	n=rd,k=3;
	for(int i=1;i<=n;i++){
		a[i]=rd;
	}
	
	for(int i=1;i<=N;i++){
		code[i]=rnd()%(1ll<<63);
	}
	
	for(int i=1;i<=n;i++){
		pre[i]=xork(pre[i-1],code[a[i]],k);
	}
	
	int cnt=0;
	mp[0]=1;
	for(int l=0,r=1;r<=n;++r){
		++num[a[r]];
		while(num[a[r]]>3){
			--num[a[l]];
			if(l>0) --mp[pre[l-1]];
			++l;
		}
		cnt+=mp[pre[r]];
		++mp[pre[r]];
	}
	
	printf("%lld",cnt);
	return Elaina;
}

\(x^2\) 问题

判断一个序列的乘积是否是 \(x^2\) ( $ x$ 为某个整数)

这个就比较好说了,根据唯一分解定理

\[x=p_1^{\alpha_1}p_2^{\alpha_2}p_3^{\alpha_3}\dots p_k^{\alpha_k} \]

其中,\(\alpha_1 \leq \alpha_2 \leq \alpha_3 \leq \dots \leq \alpha_k\)皆素数。

\[\begin{align*} x^2&=(p_1^{\alpha_1}p_2^{\alpha_2}p_3^{\alpha_3}\dots p_k^{\alpha_k})^2\\ &=p_1^{2\times\alpha_1}p_2^{2\times\alpha_2}p_3^{2\times\alpha_3}\dots p_k^{2\times\alpha_k} \end{align*} \]

那么当这个序列质因子出现的次数都是偶数次即可。

练习

题目 类型
Prefix Equality AtCoder - abc250_e 组合问题
The Number of Subpermutations CodeForces - 1175F 组合问题
The Untended Antiquity CodeForces - 869E 组合问题
由乃与大母神原型和偶像崇拜 洛谷 - P3792 组合问题
Number Game on a Tree HackerRank 出现次数问题
Quadratic Set CodeForces - 1622F \(x^2\)问题

尾图

蒂蒂~❤ 嘿嘿嘿 我的蒂蒂❤

大人~ 制作不易喵~ 赏个赞吧喵~

From:https://www.cnblogs.com/Elaina-0/p/18424332
本文地址: http://www.shuzixingkong.net/article/2261
0评论
提交 加载更多评论
其他文章 除了递归算法,要如何优化实现文件搜索功能
大家好,我是 V 哥,今天的文章来聊一聊 Java实现文件搜索功能,并且比较递归算法、迭代方式和Memoization技术的优缺点。 以下是一个使用 Java 实现的文件搜索功能,它会在指定目录及其子目录中搜索包含特定关键字的文件。此实现使用递归方式遍历目录,并可以使用文件名或内容搜索文件。 使用递
.NET 8 + Vue/UniApp 高性能前后端分离框架
前言 作为一名开发者,我们知道能够简化开发流程、提升工作效率的工具是至关重要的。 推荐一款前后端分离框架 Admin.NET(ZRAdmin),它不仅可以满足项目开发的需求,还应用了一些新的特性,如RBAC权限管理、SqlSugar ORM、以及Vue3的动态国际化支持,代码简洁易用。 接下来,让我
.NET 8 + Vue/UniApp 高性能前后端分离框架 .NET 8 + Vue/UniApp 高性能前后端分离框架 .NET 8 + Vue/UniApp 高性能前后端分离框架
redisson内存泄漏问题排查
问题描述 最近生产有个服务突然出现频繁告警,接口P99响应时间变长,运维同学观察到相应的pod cpu飙升,内存占用很高。 cpu升高问题排查是老生常谈的话题了,一般可以使用top -p pid -H查看是哪个线程占用cpu高,再结合jstack找到对应的java线程代码。 不过经验告诉我们,cpu
redisson内存泄漏问题排查 redisson内存泄漏问题排查 redisson内存泄漏问题排查
MoNA:复用跨模态预训练模型,少样本模态的福音 | ICML'24
跨模态转移旨在利用大型预训练模型来完成可能不属于预训练数据模态的任务。现有的研究在将经典微调扩展到跨模态场景方面取得了一定的成功,但仍然缺乏对模态差距对转移的影响的理解。在这项工作中,进行了一系列关于转移过程中源表示质量的实验,揭示了更大的模态差距与较少知识重用之间的联系,这意味着转移效果不佳。然后
MoNA:复用跨模态预训练模型,少样本模态的福音 | ICML'24 MoNA:复用跨模态预训练模型,少样本模态的福音 | ICML'24 MoNA:复用跨模态预训练模型,少样本模态的福音 | ICML'24
python3 numpy的一些小知识点
简介 一个用python实现的科学计算,包括: 1、一个强大的N维数组对象Array; 2、比较成熟的(广播)函数库; 3、用于整合C/C++和Fortran代码的工具包; 4、实用的线性代数、傅里叶变换和随机数生成函数。 numpy和稀疏矩阵运算包scipy配合使用更加方便。NumPy(Numer
flink 大批量任务提交 yarn 失败问题
问题现象 用户迁移到新集群后,反馈他们开发平台大量 flink 任务提交失败了,当时集群的 yarn 资源是足够的 排查过程 用户是在他们的开发平台上提交的,查看他们失败的任务,发现是他们提交端主动 Kill 的,接着沟通发现他们提交平台有个逻辑就是提交到 yarn 的 flink 任务,如果在 2
flink 大批量任务提交  yarn 失败问题 flink 大批量任务提交  yarn 失败问题
Hugging Face 论文平台 Daily Papers 功能全解析
文/ Adeena, 在快速发展的研究领域,保持对最新进展的关注至关重要。为了帮助开发者和研究人员跟踪 AI 领域的前沿动态,Hugging Face 推出了 Daily Papers 页面。自发布以来,Daily Papers 已展示了由 AK 和社区研究人员精心挑选的高质量研究。在过去一年里,已
Hugging Face 论文平台 Daily Papers 功能全解析 Hugging Face 论文平台 Daily Papers 功能全解析 Hugging Face 论文平台 Daily Papers 功能全解析
基于SqlAlchemy+Pydantic+FastApi的Python开发框架
随着大环境的跨平台需求越来越多,对与开发环境和实际运行环境都有跨平台的需求,Python开发和部署上都是跨平台的,本篇随笔介绍基于SqlAlchemy+Pydantic+FastApi的Python开发框架的技术细节,以及一些技术总结。
基于SqlAlchemy+Pydantic+FastApi的Python开发框架 基于SqlAlchemy+Pydantic+FastApi的Python开发框架 基于SqlAlchemy+Pydantic+FastApi的Python开发框架