HDU6053 TrickGCD

来源:互联网 发布:数据恢复高级技术 编辑:程序博客网 时间:2024/05/22 12:24

题目

TrickGCD

Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 2486    Accepted Submission(s): 955


Problem Description
You are given an array A , and Zhu wants to know there are how many different array B satisfy the following conditions?

1BiAi
* For each pair( l , r ) (1lrn) , gcd(bl,bl+1...br)2
 

Input
The first line is an integer T(1T10) describe the number of test cases.

Each test case begins with an integer number n describe the size of array A.

Then a line contains n numbers describe each element of A

You can assume that 1n,Ai105
 

Output
For the kth test case , first output "Case #k: " , then output an integer as answer in a single line . because the answer may be large , so you are only need to output answer mod 109+7
 

Sample Input
144 4 4 4
 

Sample Output
Case #1: 17

题目大意

     

    给你一个数组A,里面是一组数据,现在我们定义了一个数组B,  ①   1<=Bi<=Ai  ② gcd(B1,B2........Bn)>=2

   让我们求满足要求的数组B,到底有多少种


解题思路


   这个题采用的是枚举,枚举出gcd=2,gcd=3,......gcd=minA(minA是数组中数值最小的数),把所有的gcd情况加起来就是我们的答案,枚举从2开始到A数组里的最小数,因为gcd是不可能大于最小数的。 

  枚举的方法

   这里拿gcd=2来举例,比如 给的数据是 4, 8, 9,只要求出4,8,9,分别是2的几倍然后累乘,这里就是2*4*4,就是这组样例gcd=2的所有可能数,在这里gcd=4也算是gcd=2了,所以才有了去重,这里有一个点要考虑就是要考虑溢出的情况代码中我有注释。

    然后就是要去重,gcd=2的时候包括了所有2的倍数的gcd,比如,4,6,8....把这些去掉就是gcd是2的情况,然后累加就可以了

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;#define LL long long#define read(a) scanf("%d",&a)const int mod=1e9+7;const int maxn=1e5+10;int A[maxn],sum[maxn],n;int case_=1;LL num[maxn],mygcd[maxn];LL quickpow(LL a,LL b){    if(b<0) return 0;    LL ret =1;    a%=mod;    while(b)    {        if(b & 1)            ret = (ret * a) % mod;        b >>= 1 ;        a = (a * a) % mod;    }    return ret;}void work(){    memset(A,0,sizeof(A));    memset(sum,0,sizeof(sum));    memset(num,0,sizeof(num));    memset(mygcd,0,sizeof(mygcd));    int x;    read(n);    int minx=1e9;    for(int i=0;i<n;i++)    {       read(x);       A[x]++;       if(x<minx)        minx=x;    }    for(int i=1;i<maxn;i++)        sum[i]=sum[i-1]+A[i];//sum数组中存的是数组A中小于等于i的数的个数    for(int i=2;i<=minx;i++)    {        num[i]=1LL;        for(int j=0;j<maxn;j+=i)        {            int a=j/i;            int b;            int minn=min(maxn-2,j+i-1);//这里处理溢出,如果溢出minn=maxn-2,这里的j+i-1有可能比maxn大,这样数组就越界了,要处理一下            b=sum[minn]-sum[j-1];//这里求出来的是 数组A中 是a倍的i 的数的个数 3的2倍的区间是 6,7,8,这里就相当于sum[8]-sum[5]            num[i]=(num[i]*quickpow((LL)a,(LL)b)%mod)%mod;            num[i]%=mod;        }    }    for(int i=minx;i>=2;i--)    {        mygcd[i]=num[i];        for(int j=2*i;j<maxn;j+=i)        {            mygcd[i]-=mygcd[j];            mygcd[i]+=mod;            mygcd[i]%=mod;        }    }    LL ans=0;    for(int i=2;i<=minx;i++)    {        ans+=mygcd[i]%mod;        ans%=mod;    }    printf("Case #%d: %d\n",case_++,ans);}int main(){    // freopen("1009.in ", "r", stdin);    // freopen("data.out", "w", stdout);    int T;    read(T);    while(T--)    {      work();    }    return 0;}