 /**//*
題意:給出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
搜索
最新評論

|
|