/**//* 题意:给出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
搜索
最新评论
|
|