一道普通的hash

题目大意:

有N个点,每加一个点,能组成多少个矩形。

4<=N<=1000,每个点横纵坐标的绝对值不会超过16000

大致思路:

由矩阵的性质得,当一个四边形为矩形时,它的两条对角线相等且平分。

那么不妨设有两条线段,端点坐标分别为(x1,y1)、(x2,y2) 和 (x3,y3)、(x4,y4),那么需要满足:

// 保证平分

1.x1+x2=x3+x4 

2.y1+y2=y3+y4 

// 保证相等

3.(x2-x1)^2+(y2-y1)^2=(x3-x4)^2,(x4-y4)^2 // 因为任何数的平方都是正数,所以就不需要abs了

我们可以假设这两条线段就是某个四边形的对角线,那么只要这两条对角线相等且平分,那么就能确定一个矩形的存在.

所以我们只需要记录每条线段的横坐标之和,纵坐标之和及两端点距离的平方,然后通过hash来判断多少条线段的横坐标之和、纵坐标之和、两端点距离的平方相等并记录答案即可。

解题步骤:

1.读入并存储每个点,让这个点与之前的所有点构成线段.

2.hash判断之前有没有和这条线段的横坐标之和、纵坐标之和、两端点距离的平方相等的线段.

3.如果有,则记录答案;如果没有,则进行标记。

代码:

#include<stdio.h>
#include<math.h>
using namespace std;

const int Mod=3000017;
int n,x[100010],y[100010],h[Mod+10],ans=0,edge=0,num[Mod+10];

int read() {
	int S=0,F=1;char C=getchar();
	for(;C<'0' || C>'9';C=getchar()) if(C=='-') F=-1;
	for(;C>='0' && C<='9';C=getchar()) S=(S<<3)+(S<<1)+C-48;
	return S*F;
}

int main() {
    n=read();
    for(int i=1;i<=n;i++) {
    	x[i]=read(),y[i]=read();
    	for(int j=1;j<i;j++) {
    		int ax=x[i]+x[j],ay=y[i]+y[j],len=pow(x[i]-x[j],2)+pow(y[i]-y[j],2); // 记录所有点组成线段的左端点之和,右端点之和,两端点长度的平方
    		int H=ax*1071+ay*1017+len*3017+Mod; // hash,模数自己取
    		H%=Mod;
    		if(!h[H]) h[H]=(++edge),num[edge]++; // 如果没有这条线段,则进行标记
    		else ans+=num[h[H]],num[h[H]]++; // 如果已经存在这条线段,则将所以能组成矩形的数目加入答案
		}
		printf("%d\n",ans);
	}
	return 0;
}

由于我的hash不够强大,所以只要强一点的数据就过不了了,所以我们需要进一步来强化hash。

hash:

1.map

map适用于字符串hash,然而这题不是字符串hash...所以过

2.双重hash

即在时间和空间优秀的情况下,进行两次及以上次数的hash。

3.存储被hash值

我们可以将被hash的值存储起来,如果原先hash的值不一样的话,就将它换一个模数。例如本题:

我们可以把不重复的每条线段的横坐标之和,纵坐标之和及两端点距离的平方记录下来。

然后每得到一个hash数就对其进行操作。

所以改进之后代码如下:

#include<stdio.h>
using namespace std;

const int Mod=3000017;
int n,x[100010],y[100010],h[Mod+10],xx[Mod+10],yy[Mod+10],l[Mod+10],num[Mod+10],ans=0,edge=0;

int read() {
	int S=0,F=1;char C=getchar();
	for(;C<'0' || C>'9';C=getchar()) if(C=='-') F=-1;
	for(;C>='0' && C<='9';C=getchar()) S=(S<<3)+(S<<1)+C-48;
	return S*F;
}

int main() {
    n=read();
    for(int i=1;i<=n;i++) {
    	x[i]=read(),y[i]=read();
    	for(int j=1;j<i;j++) {
    		int ax=x[i]+x[j],ay=y[i]+y[j],len=pow(x[i]-x[j],2)+pow(y[i]-y[j],2);
    		int H=ax*1071+ay*1017+len*3017+Mod;
    		H%=Mod;
			while (h[H] && (xx[h[H]]!=ax || yy[h[H]]!=ay || l[h[H]]!=len))
				H=(H+19260817)%Mod;
    		if(!h[H]) {
    			h[H]=(++edge);
				num[edge]++;
    			xx[edge]=ax;
    			yy[edge]=ay;
    			l[edge]=len;
			}
    		else ans+=num[h[H]],num[h[H]]++;
		}
		printf("%d\n",ans);
	}
	return 0;
}

 

4.long long自然溢出

将被hash数乘一个很大的数,让它爆unsigned long long的范围,然后它就会溢出,成为一个值。这个值就可以当做为hash值。

5.未完待续...

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值