哈希学习笔记

先上一道例题:
POJ1840 Eqs

考虑具有以下形式的方程: a1x13 + a2x23 + a3x33 + a4x43 + a5x53 = 0
系数是从区间[-50,50]起的整数。 考虑解决方案(x1,x2,x3,x4,x5)来验证方程xi∈[-50,50],xi!=
0,任何i∈{1,2,3,4,5 }。

确定满足给定方程的解数。 输入项 输入的唯一行包含由空格分隔的5个系数a1,a2,a3,a4,a5。 输出量
输出将在第一行包含给定方程的解数。
Sample Input

37 29 41 43 47
Sample Output

654

直观思路就是一个个枚举,时间复杂度是O(n^5)
实际上,我们可以把它变形成:
a1x13+a2x23+a3x23=-(a4x43+a5x53)
分两部分处理,一次处理左边哈希,一次枚举右边进行判断
因为同一个值出现有多种情况,所以还要储存次数:
Code:

#include<iostream>
using namespace std;
const unsigned long long MAXN=999999999;
const int p=133301;
int h[1000001],num[1000001],val[1000001],nxt[1000001],tot;
void Put(long long x){
	int k=(x+MAXN)%p,flag=1,i;
	for(i=h[k];i;i=nxt[i]){
		if(val[i]==x){
			flag=0;break;
		}
	}
	if(flag){
		nxt[++tot]=h[k];
		num[tot]=1;
		val[tot]=x;h[k]=tot;
	}else num[i]++;//储存次数
}
int Find(int x){
	int k=(x+MAXN)%p,flag=1,i;
	for(i=h[k];i;i=nxt[i]){
		if(val[i]==x){
			flag=0;break;
		}
	}
	if(flag) return 0;
	else return num[i];
}
int a[5];
long long getx(long long x){
	return x*x*x;
}
int ans;
int main(){
	for(int i=0;i<5;i++) cin>>a[i];
	for(int i=-50;i<=50;i++)
		if(i!=0)
		for(int j=-50;j<=50;j++)
			if(j!=0)
			for(int k=-50;k<=50;k++)
				if(k!=0)
					Put(a[0]*getx(i)+a[1]*getx(j)+a[2]*getx(k));//第一部分
	for(int i=-50;i<=50;i++)
		if(i!=0)
		for(int j=-50;j<=50;j++){
				if(j!=0)
					ans+=Find(-(a[3]*getx(i)+a[4]*getx(j)));
		}//第二部分
	cout<<ans;
	return 0;
}

时间复杂度是O(n3+n2)

然后是一道类似的:
POJ2785 4个总和为0的值

描述

SUM问题可以表示为:给定四个具有整数值的列表A,B,C,D,计算多少个四元组(a,b,c,d)∈A x B x C x D使得a + b

  • c + d = 0。在下文中,我们假定所有列表的大小均相同。 输入项

输入文件的第一行包含列表n的大小(该值可以最大为4000)。然后,我们有n行包含四个分别属于A,B,C和D的整数值(绝对值最大为2 28)。
输出量

对于每个输入文件,您的程序必须编写四倍数,其总和为零。 样本输入

6
-45 22 42 -16
-41 -27 56 30
-36 53 -37 77
-36 30 -75 -46 26 -38 -10 62
-32 -54 -6 45 样本输出

5

同样的思路,我们分两拨处理
Code:

#include<iostream>
using namespace std;
const unsigned long long MAXN=999999999;
const int p=4000*4000+7;//这里的素数很毒
int h[p+10],nxt[p+10],num[p+10],val[p+10],tot;
inline void add(int k,int x){
	nxt[++tot]=h[k];
	h[k]=tot;
	val[tot]=x;
}
void put(int x){
	int k=(x+MAXN)%p,flag=1,i;
	for(i=h[k];i;i=nxt[i]){
		if(val[i]==x){
			flag=0;break;
		}
	}if(flag){
		add(k,x);
		i=tot;
	}num[i]++;
}
int Find(int x){
	int k=(x+MAXN)%p,flag=1,i;
	for(i=h[k];i;i=nxt[i]){
		if(val[i]==x){
			flag=0;break;
		}
	}if(flag) return 0;
	else return num[i];
}
int a1[4001],a2[4001],a3[4001],a4[4001],n;
int ans;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a1[i]>>a2[i]>>a3[i]>>a4[i];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++) put(a1[i]+a2[j]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++) ans+=Find(-(a3[i]+a4[j]));
	cout<<ans;
}

未完待续

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值