解题思路:链式哈希
链式hash表实现的数据结构,实际上没有使用链表,而是使用数组,用数组索引代替指针(原因是在POJ中一次开很大的数组是允许的,这样可以避免每次动态申请空间的时间开销,算是用空间换时间)head用做索引,指向hash值为key的第一个cnt;
下面是 hash 链表的插入过程:
void Insert(int num)
{
int key = hash(num);
for(int i = head[key]; i != -1; i = edge[i].next)
{
if(edge[i].v == num)
{
edge[i].cnt++;
return ;
}
}
edge[tot].v = num;
edge[tot].cnt = 1;
edge[tot].next = head[key];
head[key] = tot++;
}
首先对num取hash,根据head找到第一个hash值为key的数在edge中的索引,如果找到,则cnt加1。否则在edge表的末尾(索引为tot,tail of table)保存下这个数的信息(数值num, 计数为1)。注意的是这里让这个新的节点下一跳指向当前的头结点,然后更新头结点为这个新节点。这样实际上将节点插入在了这一系列值的最前列。体会下当head[s]为-1时,即没有值时的情况。这样写要比将新节点报错在链式表的末端要统一。
edge中的每一个元素是一个结构体,保存这个元素的num(用v表示),下一跳在edge数组中的索引,还有num出现了多少次(用cnt表示)。hash值取0~3999970(见hash函数中的取余素数)。所以两个数组开3999971这么大。
下面是查找过程:
int find(int num)
{
int key = hash(num);
for(int i = head[key]; i != -1; i = edge[i].next)
{
if(edge[i].v == num)
{
return edge[i].cnt;
}
}
return 0;
}
下面是完整代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 4100;
struct edge
{
int v;
int next;
int cnt;
} edge[3999972];
int head[3999972];
int hash(int num)
{
return (num + 3899971) % 3999971;
}
int tot;
int a[maxn] , b[maxn];
int c[maxn] , d[maxn];
void Insert(int num)
{
int key = hash(num);
for(int i = head[key]; i != -1; i = edge[i].next)
{
if(edge[i].v == num)
{
edge[i].cnt++;
return ;
}
}
edge[tot].v = num;
edge[tot].cnt = 1;
edge[tot].next = head[key];
head[key] = tot++;
}
int find(int num)
{
int key = hash(num);
for(int i = head[key]; i != -1; i = edge[i].next)
{
if(edge[i].v == num)
{
return edge[i].cnt;
}
}
return 0;
}
int main()
{
//freopen("in.txt","r",stdin);
int n,i,j;
while(cin>>n)
{
tot=0;
memset(head,-1,sizeof(head));
for(i = 1; i <= n; i++)
cin>>a[i]>>b[i]>>c[i]>>d[i];
for(i = 1; i <= n; i++)
{
for(j = 1; j <= n; j++)
{
Insert(a[i] + b[j]);
}
}
__int64 ans = 0;
for(i = 1; i <= n; i++)
{
for(j = 1; j <= n; j++)
{
ans += find(- c[i] - d[j]);
}
}
cout<<ans<<endl;
}
return 0;
}