友好数对

1 问题描述
⼩X 是⼀个有天⽣数感的好孩⼦,他总能从⼀些看似⽆关的数中找出友好关系。
⼩X 认为两个数x 和y 有友好关系,当且仅当x xor y 在⼆进制表示下恰有两位是1。这
⾥xor 是指按位异或。现在⼩X 给你两组数fa1; a2; : : : ; ang 和fb1; b2; : : : ; bmg,问你有多少
对(i; j),满⾜
1 6 i 6 n; 1 6 j 6 m; ai和bj有友好关系。
2 输入格式
输⼊⽂件名为bipartite.in。
第⼀⾏为两个正整数n;m。
接下来⼀⾏n 个⾮负整数,表示a1 an。
接下来⼀⾏m 个⾮负整数,表示b1 bm。
3 输出格式
输出⽂件名为bipartite.out。
输出⼀⾏⼀个整数,表示友好数对的个数。
4 样例
⻅下发/bipartite/bipartite1.in(out),/bipartite/bipartite2.in(out)。
5 数据规模与约定
对于60% 的数据,满⾜n;m 6 2000。
对于80% 的数据,满⾜n;m 6 15000。
对于100% 的数据,满⾜n;m 6 100000; 0 6 ai; bi < 230。
6 提示
xor 0 1
0 0 1
1 1 0
按位异或是指对于两个⼆进制数,按最低位对⻬,⾼位不⾜补0,每位按照以上法则得出结
果后,在⼆进制下组成⼀个数。


观察要求可知,(ai xor 2k) xor (bj xor 2l) = 0; (k ̸= l)。
对于每个ai,枚举哪位是1 加⼊哈希。对于每个bj,枚举哪位是
1,在哈希中查询个数。
最后,减去本来ai = bj 的情况后除以2。
假设哈希的时间复杂度为单次O(1),那么总时间复杂度
O(n log X)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#define f(i,l,r) for(i=(l);i<=(r);i++)
using namespace std;
const int MAXN=100005,MOD=10000019;
struct Edge{
	int v,next,w;
}e[4000005];
int n,m,a[MAXN],b[MAXN],head[MOD],tot;
long long ans=0;
inline void add(int x)
{
	int i,ha=x%MOD;
	for(i=head[ha];~i;i=e[i].next){
		if(e[i].v==x){
			e[i].w++;
			return;
		}
	}
	e[tot].v=x;
	e[tot].w=1;
	e[tot].next=head[ha];
	head[ha]=tot++;
}
inline int query(int x)
{
	int i,ha=x%MOD;
	for(i=head[ha];~i;i=e[i].next){
		if(e[i].v==x){
			return e[i].w;
		}
	}
	return 0;
}
int main()
{
	freopen("bipartite.in","r",stdin);
	freopen("bipartite.out","w",stdout);
	int i,j;
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	f(i,1,n){
		scanf("%d",&a[i]);
	 	add(a[i]);
	}
	f(i,1,m){
		scanf("%d",&b[i]);
		ans-=30*query(b[i]);
	}
	memset(head,-1,sizeof(head));
	f(i,1,n){
		f(j,0,29){
			add(a[i]^(1<<j));
		}
	}
	f(i,1,m){
		f(j,0,29){
			ans+=query(b[i]^(1<<j));
		}
	}
	printf("%lld\n",ans/2);	
	return 0;
} 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值