4 2 3 4 5 4 2 4 6 8 7 2 3 4 5 7 6 8
1 0 34
这道题的题意是输入一个数N,再输入N个1到10000的数,从这N个数中选出4个数,使得这四个数的公因数为1。求有多少组这样的四个数。
举个例子来说明吧,这个题目只要明白了做法, 就很好做了。不明白做法,硬看很难看懂。
假如输入的是 4 30 30 30 30 因为公因数不只有1,所以答案是0。
这道题可以用逆向的思维来求,先求出公因数不只有1的情况,再用所有的情况减去不只有1的情况。所有的情况可以用组合数在求,即 for(i=4;i<maxn;i++) node[i]=i*(i-1)*(i-2)*(i-3)/24; node[i]指的是i个数(i>=4)选取4个有node[i]种方式来选取。将四个数分解质因数,求得质因数为2、3、5,并保存在prime数组中,prime[0]=2,prime[1]=3,prime[2]=5。用num[i]数组统计i因子在N个数的所有质因子中的个数,此样例为num[2 3 5 6 10 15 30]=4,其余均为0。用vist[i]数组来统计i因子是奇数还是偶数。最后奇加偶减,用ans来表示公因数不只有1的个数,最后用之前求得组合数组node[N]减去ans,求出最后结果。因为数据可能比较大,所以用的long long型。
这是测试的数据。
最后附上代码,代码不是我敲得,是我改的别人的,测试所用的语句我注释掉了,想要测试的可以取消注释。
/*
4
2 3 4 5
4
2 4 6 8
7
2 3 4 5 7 6 8
4
30 30 30 30
4
30 30 30 21
*/
#include<stdio.h>
#include<string.h>
using namespace std;
#define LL long long
#define maxn 10005
LL node[maxn],num[maxn],vist[maxn],prime[maxn];
void Init()
{
LL i;
for(i=4;i<maxn;i++)//求组合数,即i个数里面选择4个数的选择的种类有几种。
node[i]=i*(i-1)*(i-2)*(i-3)/24;
}
void make_count(int m)
{
int i,j,tmp,flag,cnt=0;
for(i=2;i*i<=m;i++)//求m的质因数,保存在prime数组里面
if(m&&m%i==0)
{
prime[cnt++]=i;
while(m&&m%i==0)
m/=i;
}
if(m>1)
prime[cnt++]=m;
//printf("cnt:%d\n",cnt);
for(i=1;i<(1<<cnt);i++)//i取1到2^3-1
{
tmp=1,flag=0;
for(j=0;j<cnt;j++) //j取0到2
if(i&(1<<j)) //意思是如果i=2^j,则为真。可以写成if(i==2^j)
flag++,tmp*=prime[j];
num[tmp]++; //统计当前因子出现的次数
vist[tmp]=flag; //记录当前因子是奇数个还是偶数个,2 3 5=1 6 10 15=2 30=3 大于1的为多了的
}
/*
printf("num[i](1<=i<=30):");
for(i=1;i<=30;i++)
printf("%d ",num[i]);
printf("\n");
printf("vist[i](1<=i<=30):");
for(i=1;i<=30;i++)
printf("%d ",vist[i]);
printf("\n");
*/
}
int main()
{
Init();
int n,i,x;
while(~scanf("%d",&n))
{
memset(num,0,sizeof(num));
memset(vist,0,sizeof(vist));
for(i=0;i<n;i++)
{
scanf("%d",&x);
make_count(x);
}
LL ans=0;
for(i=1;i<maxn;i++)
if(num[i]) //如果存在这样的因子,即因子出现次数大于0
{
if(vist[i]&1) //当前因子为奇数,加
ans+=node[num[i]];
else //当前因子为偶数,减
ans-=node[num[i]];
}
printf("%I64d\n",node[n]-ans);
}
return 0;
}