关于异或哈希

news/2024/11/16 11:24:47/文章来源:https://www.cnblogs.com/Elaina-0/p/18424332

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\)问题

尾图

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/802764.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

threejs 使用base64编码的图片作为贴图

使用base64作为贴图可以从接口直接传输(如果特别大需要压缩),可以省去很多操作 代码如下// 纹理加载器 const texLoader = new THREE.TextureLoader(); const base64Str = "data:image/png;base64,..."; texLoader.load(base64Str, (texture) => {const aspectR…

WPS在堆积柱状图上显示加总数据

我们常常会用到堆积柱状图,每个色块都可以显示数值,那如何在图上显示加总数据呢?今天分享一个小方法。以2024年前三周手机销量为例:首先将数据整理如下,最后一列求和。2. 选择所有数据(含合计)绘制堆积柱状图,并选中“合计”的柱状图,右键添加数据标签。3. 选择“合计”…

Vue3 注册及使用全局方法

一、背景 自己的一个考公网站,需求是 用户登录系统,但是用户去查看功能时要判断当前用户是否有是会员,如果不是会员,那么查看其他功能时需要弹窗说不是vip,如果是会员则可以直接查看。 二、实现 首先上截图。 1.Vue3 全局注册方法 1.新建popToast.jsimport Cookies from &q…

Figma UI Design add background color to text All In One

Figma UI Design add background color to text All In One 如何使用 Figma 给文字添加背景色Figma UI Design add background color to text All In One如何使用 Figma 给文字添加背景色https://www.figma.com/design/ solutionshttps://www.youtube.com/watch?v=j1UT8ezXAXI…

app手机元素定位方式

方式一:采用uiautomatorviewer元素定位 ①这个定位方式有缺陷也就是它只能识别Android8以下的系统,8及以上的系统就无法定位了 ②位置:这个工具是位于安装的SDK下的tools文件下,我的安装路径如下③双击打开这个工具就行,再运行的页面点击 方式二:安装Appium-Inspector ①下…

从0到0.1学习实践盒子模型

从0到0.1学习实践盒子模型 盒子模型概念 预备知识margin:外边距【两个元素之间的距离】 border:边框 padding:内边距【内容区域和边框距离】 height:文本高度 width:文本宽度content-box (形象图片)content-box--只计算内容区域的宽度和高度,边框和内边距不算在内。 .b…

Leetcode 445. 两数相加 II

1.题目基本信息 1.1.题目描述 给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。 你可以假设除了数字 0 之外,这两个数字都不会以零开头。 1.2.题目地址 https://leetcode.cn/problems/add…

Windows命令:时间延迟命令

延迟一段时间再执行下一条命令”。 一、利用ping实现延迟命令 这种延时手段是不精确的,因为每一次ping通的延迟不一样。 1、示例chcp 65001 @echo off echo 延时前:%time% ping /n 3 127.0.0.1 >nul echo 延时后:%time% pause 参数/n表示ping通的次数。127.0.0.1是本机ip…

电力施工作业绝缘手套识别系统

电力施工作业绝缘手套识别系统对电力作业人员在电力设备上进行施工作业时是否佩戴绝缘手套进行识别分析,当电力施工作业绝缘手套识别系统检测到作业人员未佩戴绝缘手套时立即抓拍存档同步回传给后台监控人员,提醒相关人员及时制止,及时规避更危险的触电事故发生。电力施工作…

sicp每日一题[2.24-2.27]

2.24-2.26没什么代码量,所以跟 2.27 一起发吧。Exercise 2.24Suppose we evaluate the expression (list 1 (list 2 (list 3 4))). Give the result printed by the interpreter, the corresponding box-and-pointer structure, and the interpretation of this as a tree (as…

ansible-cmdb简单使用

1、安装 官方:https://ansible-cmdb.readthedocs.io/en/latest/ wget https://github.com/fboender/ansible-cmdb/releases/download/1.27/ansible-cmdb-1.27-2.noarch.rpm yum -y install ./ansible-cmdb-1.27-2.noarch.rpm2、使用 首先,为你的主机生成 Asible 输出: mkdir…

第三周作业(海报➕二维码)

二维码可以在那个网站上面学习数学知识