SPOJ:NO GCD (求集合&秒啊)
You are given N(1<=N<=100000) integers. Each integer is square free(meaning it has no divisor which is a square number except 1) and all the prime factors are less than 50. You have to find out the number of pairs are there such that their gcd is 1 or a prime number. Note that (i,j) and (j,i) are different pairs if i and j are different.
Input
The first line contains an integer T(1<=T<=10) , the number of tests. Then T tests follows. First line of each tests contain an integer N. The next line follows N integers.
Output
Print T lines. In each line print the required result.
Sample Input |
Sample Output |
1 3 2 1 6 |
8 |
Explanation
gcd(1,2)=1
gcd(2,1)=1
gcd(2,6)=2, a prime number
gcd(6,2)=2, a prime number
gcd(1,6)=1
gcd(6,1)=1
gcd(2,2)=2, a prime number
gcd(1,1)=1
So, total of 8 pairs.
题意:给定数组a[],求多少对(i,j),使得a[i],a[j]互质或者gcd是质数,保证a[]只有小于50的素因子,而且不含平方因子。
思路:注意到只有15个素数,开始想到了用二进制来找互质的个数和有一个素因子的个数,但是复杂度好像还是过不去。第二天忍不住参考了vj上面的代码。。。
主要问题在于,如何快速地求一个二进制的子集,即对i,求所有的j,j<=i&&(i|j)==i。后面地就不难。
前辈写的是:
for(i=0;i<M;i++){ for(j=i;;j=(j-1)&i){ s[i]+=num[j]; //关键,得到子集 if(!j) break; } }
时间大概是1.4e7。
int times=0; for(i=0;i<M;i++){ for(j=i;;j=(j-1)&i){ times++; if(!j) break; } } cout<<times<<endl;
。。。注意把0也要累加进去。
#include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define ll long long using namespace std; const int M=1<<15; int num[M],s[M]; int p[15]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47}; int main() { int T,N,i,j,tmp; ll ans,x; scanf("%d",&T); while(T--){ scanf("%d",&N); memset(num,0,sizeof(num)); memset(s,0,sizeof(s)); for(i=1;i<=N;i++){ scanf("%lld",&x); tmp=0; for(j=0;j<15;j++) if(x%p[j]==0) tmp+=1<<j; num[tmp]++; } for(i=0;i<M;i++){ for(j=i;;j=(j-1)&i){ s[i]+=num[j]; //关键,得到子集 if(!j) break; } } ans=0; for(i=0;i<M;i++){ ans+=(ll)num[i]*s[i^(M-1)];//互质 for(j=0;j<15;j++){ //刚好有一个素因子 if(i&1<<j){ ans+=(ll)num[i]*(s[i^(M-1)^(1<<j)]-s[i^(M-1)]);//减法保证这个素因子不被减去 } } } cout<<ans<<endl; } return 0; }