 /**//*
题意:给出n个数,从中取4个数,求多少种方案使得4个数的gcd为1

理解pkkj wiki代码的

4个数的gcd为1,则4个数没有共同的>1的因子
从反面做,枚举共同的>1的因子i,然后从这些数中取4个
这些共同的因子如2,3,6 这样子会重复计算,用容斥
由于值域较小,每次读入x,对其分解,然后其所有的因子 +1
如24 = 2^3*3 ,其因子就为2,3,6了
这些共同因子是p1*p2..,每个素数因子的幂只能是1,这样子各元素独立的容斥比较方便做
(如果pi的幂>1时,容斥的集合就多了,而且容斥时要取lcm , zoj 3233需要lcm)

答案就是 C(N,4) - 有共同的>1的因子的方案数

问了watashi,他说得更清晰
“记f(x)为每个元素都整除x的组合方案数,那么
answer:=f(1)-f(2)-f(3)-…-f(p)+f(2*3)+f(2*5)+…+f(p1*p2)-f(2*3*5)….
算法应该就是这样吧,应该可以nsqrt(n),如果先预处理质数表可以更快”
*/
#include<cstdio>
#include<cstring>
#include<assert.h>
#include<algorithm>

using namespace std;

const int MAXSIEVE = 10001;
const int MAXN = 10001;

bool sieve[MAXSIEVE] , isOdd[MAXN];
long long C4[MAXN];
int npr,pr[MAXN];
int pi[MAXN];
int sum[MAXN];


pair<int,int> frac(int n)
  {
int cnt = 0 , tot = 0;
for(int i = 0;i<npr && n>1 ;i++)
 {
if( n % pr[i] )continue;
pi[cnt++] = pr[i];
while(n%pr[i] == 0)tot++,n/=pr[i];
}
return make_pair(cnt,tot);
}

void getAll(int n,int x)
  {
if(n==0)
 {
sum[x]++;
return;
}
getAll(n-1,x);
getAll(n-1,x*pi[n-1]);
}

void init()
  {
//sieve
for( int i=2; i < MAXSIEVE ; i++ )
 {
if( sieve[i] )continue;
pr[npr++] = i;
for( int j = i+i; j<MAXSIEVE ; j += i )sieve[j] = true;
}
for(int i = 2; i<MAXN; i++)
 {
int tot = frac(i).second;
isOdd[i] = tot & 1;
}
//comb
for(int i = 0; i<4 ;i++)
 {
C4[i] = 0;
}
for(int i = 4; i < MAXN ; i ++)
 {
long long div = 1,mul = 1;
for(int j = 0; j < 4; j++)
 {
mul *= (i-j);
div *= j + 1;
}
C4[i] = mul/div;
}
}

void sovle(int n)
  {
int x;
for(int i = 2; i<MAXN; i++)
 {
sum[i] = 0;
}
for(int i = 0;i < n; i++)//每次读入一个数时,求出其所有因子,然后计数
 {
scanf("%d",&x);
int cnt = frac(x).first;
getAll(cnt,1);
}
long long ans = 0;
for(int i = 2;i<MAXN;i++)
 {
//if(sum[i]<4)continue;
//因子个数奇数的+ 偶数的-
if(isOdd[i])ans += C4[sum[i]];
else ans -= C4[sum[i]];
}
printf("%lld\n",C4[n] - ans);
}

int main()
  {
#ifdef ONLINE_JUDGE
#else
freopen("in","r",stdin);
#endif
init();
int n;
while( ~scanf("%d",&n) )
 {
sovle(n);
}
return 0;
}
|
|
常用链接
随笔分类
Links
搜索
最新评论

|
|