友好数对(kompici)
题目描述
在顺利完成家庭作业以后,Mirko感到非常的厌倦。所以,他列出了N个数,这些数中有些数对他是喜欢的,有些数对他是不喜欢的。
他喜欢的数对叫做友好数对,如果两个数至少有一个相同的数字(不要求在相同的位置),那么这两个数就是友好数对。请帮助Mirko在这N个数找出有多少友好数对。
输入
第一行一个正整数N(1<=N<=1000000)。
接下来N行,每行一个正整数,范围在1到1018之间。N个数中任意两个数都是不同的。
输出
只有一行一个整数,表示友好数对的个数。
这道题正解是用容斥原理+位运算
当然正解也是靠慢慢得部分分得出的。
20~40%
对于20~40分,我们采用最普通的暴力,我们用双重循环来循环暴力每两个数。
对于a[i],a[j] (a[ ]表示我们输入的数),我们将它们每一位都进行分解,然后看看是否有相同的数,如果有则ans++,没有则继续进行循环。
code:
#include<cstdio>
#include<iostream>
using namespace std;
long a[1000010],b[20],c[20];
int main()
{
freopen("kompici.in","r",stdin);
freopen("kompici.out","w",stdout);
long long n,ans=0;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<n;i++)
{
for(int j=i+1;j<=n;j++)
{
long long t=a[i],tt=a[j],te1=0,te2=0;
while(t!=0)
{
b[++te1]=t%10;
t/=10;
}
while(tt!=0)
{
c[++te2]=tt%10;
tt/=10;
}
bool f=false;
for(int _=1;_<=te1;_++)
{
for(int __=1;__<=te2;__++)
{
if(b[_]==c[__])
{
ans++;
f=true;
break;
}
}
if(f==true) break;
}
}
}
cout<<ans<<endl;
return 0;
}
100%
我们使用容斥原理+位运算
对于我们每次输入的数,我们自己定义一个二进制数(九位二进制数),
然后我们在将它转为十进制,桶++,桶的范围是1024,
因为我们我们最大也就是每位都是1(2的10次方-1)。
举例来说:
假设这个数是23
那我们的二进制数则是1100
why
先讲一下我们自己定义的二进制数:
这个数有我们原先输入的数转变而成。我们
将原先的数每一位进行分解,每次分解的数(设它为t),
然后t与二进制的第t+1进行或运算,为什么是t+1,
因为我们要考虑0.之所以用或运算,因为题目只规定了判断两个数之间是否有相同数,并没要你求有几个相同。例如:4 和 44,只让你求他们的相同数是4,没让你求有多少个。
OK,我们讲完预处理接着将容斥操作。
我们用双重循环来进行遍历
for i=1;i<=1023;i++
for j=1;j<=1023;j++
然后,我们判断i与j是否有相同的数
并我们输入的有这个数,重中之重还要
判断i!=j.
if(i&j && b[j]!=0 && i!=j)
然后条件如果成立
则将他们两的出现次数乘积加到ans中
这样就完了吗?
不可能。我们按照老方法,举个栗子。
4和44,尽管他们都化为16(10000)并加了出现次数,但是他们
还可以进行一次匹配,所以每次i循环末都要将答案加b[i]*b[i-1];
最后输出答案(记得除以二哦!)
我讲明白了吗?
code:
#include<iostream>
#include<cstdio>
using namespace std;
long long n,b[1024],k,ans,s,x;
int main()
{
freopen("kompici.in","r",stdin);
freopen("kompici.out","w",stdout);
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%lld",&x);
//预处理
s=0;
while(x!=0)
{
k=x%10;
x=x/10;
s=s|(1<<k);
}
b[s]++;
}
//容斥操作
for(int i=1; i<=1023; i++)
{
for(int j=1; j<=1023; j++)
if(i&j&&b[j]!=0&&i!=j) ans=ans+b[i]*b[j];
ans=ans+b[i]*(b[i]-1);
}
printf("%lld",ans/2);
//为什么要除二,因为i之前是5j是55,而后面i可以变成55j变成5,故要除2!
return 0;
}