T422522 k耦合

题目描述

若两个数x,yx,y,他们在二进制形式下有kk位是不同的,我们称这两个数字是kk耦合的。

例如二进制1001100101100110是满足44耦合的,他们有44位不同

现在给出一个非负整数序列a1,a2,.....,ana_1,a_2,.....,a_n,你需要求存在多少对(i,j),i<j(i,j),i<j使得ai,aja_i,a_j是满足kk耦合的。

输入格式

第一行输入n,kn,k,表示序列长度和耦合要求

第二行输入nn个数,表示序列aa

输出格式

输出一个数字表示答案

样例 #1

样例输入 #1

text
1
2
4 1
0 3 2 1

样例输出 #1

text
1
4

样例 #2

样例输入 #2

text
1
2
6 0
200 100 100 100 200 200

样例输出 #2

text
1
6

提示

数据范围:

(2n105,0k14)(2 ≤ n ≤ 10^5, 0 ≤ k ≤ 14)

(0ai104)(0 ≤ a_i ≤ 10^4)


题解

首先最先想到的是枚举ii,jj判断aia_i,aja_j是否为k耦合。时间复杂度尾或者O(n2)O(n^2)。得到的结果是超超时。考虑优化,我们发现题目所给的值域只有10410^4,也就是说会出现重复的数字,我们对0到10000依次枚举计算是不会超时的,所以我们需要统计重复的数字来避免重复计算,减少时间。

首先我们可以创建一大小为略大于10410^4数组,下表表示这个数,对应的值表示出现的次数。

1
int mp[10004]={0};

然后我们依次枚举计算是否符合条件即可。

k耦合的判断方法为aia_i^aja_j,因为考虑异或二进制位上相同则为0,考虑两数字异或后有二进制上有几个1即可。计算一个数的二进制上有几个一有2种办法:

法一:mod2法

1
2
3
4
5
6
7
8
9
10
11
12
int a;
int cnt=0;
cin>>a;
while (a!=0)
{
if (a%2==1)
{
cnt++;
}
a/=2;
}
cout<<cnt<<endl;

法二:位运算法

1
2
3
4
5
6
7
8
9
int b;
int cnt=0;
cin>>b;
while (b!=0)
{
cnt+=b&1;
b>>=1;
}
cout<<cnt<<endl;

下面是完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include<bits/stdc++.h>
using namespace std;
bool check(int a,int k)
{
int b=a;
int cnt=0;
while (b!=0)
{
cnt+=b&1;
b>>=1;
}
return cnt==k;
}
int main()
{
int n,k;
long long sum=0;
cin>>n>>k;
int temp;
int mp[10004]={0};
for (int i=1;i<=n;i++)
{
cin>>temp;
mp[temp]++;
}
if (k==0)
{
for (int i=0;i<=10000;i++)
{
if (mp[i]>0)
{
sum+=mp[i]*(mp[i]-1);
}
}
sum/=2;
}
else
{
for (int i=0;i<10000;i++)
{
if (mp[i]>0)
{
for (int j=i+1;j<=10000;j++)
{
if (mp[j]>0)
{
if (check(i^j,k))
{
sum+=mp[i]*mp[j];
}
}
}
}
}
}
cout<<sum<<endl;
return 0;
}

T422522 k耦合
https://serendipity565.github.io/posts/f90aa7c963e7/
作者
Serendipity
发布于
2024年2月4日
许可协议
BY-SERENDIPITY565