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;
            }
        }

。。。注意把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;
}

原文地址:https://www.cnblogs.com/hua-dong/p/8966526.html

时间: 2025-01-04 16:29:26

SPOJ:NO GCD (求集合&秒啊)的相关文章

spoj 3871 gcd extreme

1 题目大意给出一个n,求sum(gcd(i,j),0<i<j<=n); 2 可以明显的看出来s[n]=s[n-1]+f[n]; 3 f[n]=sum(gcd(i,n),0<i<n); 4 现在麻烦的是求f[n] 5 gcd(x,n)的值都是n的约数,则f[n]= 6 sum{i*g(n,i),i是n的约数},注意到gcd(x,n)=i的 7 充要条件是gcd(x/i,n/i)=1,因此满足条件的 8 x/i有phi(n/i)个,说明gcd(n,i)=phi(n/i). 9

求集合元素的所有非空子集

现有一个包含N个元素的集合S,求集合S的所有子集? 例如:集合S包含三个元素{a, b, c},则它的所有子集为:{ }(空集), {a}, {b}, {c}, {a, b}, {a, c}, {b, c} 和{a, b, c}. 这里先用位操作的思路来求解,具体方法:用2进制Bit位来标记集合中的某个元素是否被选中,1代表选中,0代表未选中.例如集合{a, b, c}的所有子集可如下表示: {a}                       0 0 1 {b}                 

hdu 1856 求集合里元素的个数 输出最大的个数是多少

求集合里元素的个数 输出最大的个数是多少 Sample Input41 23 45 61 641 23 45 67 8 Sample Output42 1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <algorithm> 5 # include <cmath> 6 # include <queue> 7 # define LL

两种求集合所有子集的方法

假设我们有一个求集合的全部子集(包含集合自身)的需求,即有一个集合s,包含两个元素 <a,b>,则其全部的子集为<a,ab,b>. 不难求得,子集个数sn与原集合元素个数n之间的关系为:sn=2^n-1. 本文分别讲述两种实现方法: 一:位图法: 1)构造一个和集合一样大小的数组A,分别与集合中的某个元素对应,数组A中的元素只有两种状态:"1"和"0",分别代表每次子集输出中集合中对应元素是否要输出,这样数组A可以看作是原集合的一个标记位图.

N个数GCD求解法

[N个数GCD求解法] 1.质因数分解法. 2.两两求解法. 3.更相减损法. 例题 4.Trick1. 5.Trick2. 6.Trick3.

(c++ 实现)山东科技大学 oj 求集合的交并补集(数据结构习题)

Problem A: 求集合的交并补集 Time Limit: 1 Sec  Memory Limit: 4 MBSubmit: 3663  Solved: 1041[Submit][Status][Web Board] Description 任意给定两个包含1-30000个元素的集合A,B(集合中元素类型为任意整型数,且严格递增排列),求A交B.A并B.A-B和B-A集合. Input 输入第一行为测试数据组数.每组测试数据两行,分别为集合A.B.每行第一个数n(1<=n<=30000)为

求集合的子集

package com.xsz.demo; /**     * @author cwqi    * @date 2014-11-19 下午9:55:31    */ public class SubSet { /**  * 求集合的子集  * @param args  */ public static void main(String[] args) { int array[] = {1,2,3}; SubSet.getSubSet(array); } private static void g

求集合的幂

<数据结构>严蔚敏 算法6.14 // exam1.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> #include <vector> using namespace std; void print_set(vector<char> s,int num) { cout<<"Subset Num"<<num<&l

两种求集合全部子集的方法

如果我们有一个求集合的所有子集(包括集合自身)的需求,即有一个集合s,包括两个元素 <a,b>,则其所有的子集为<a,ab,b>. 不难求得,子集个数sn与原集合元素个数n之间的关系为:sn=2^n-1. 本文分别讲述两种实现方法: 一:位图法: 1)构造一个和集合一样大小的数组A,分别与集合中的某个元素相应,数组A中的元素仅仅有两种状态:"1"和"0",分别代表每次子集输出中集合中相应元素是否要输出.这样数组A能够看作是原集合的一个标记位图