HDU-6053 TrickGCD

来源:互联网 发布:淘宝开店考试题 编辑:程序博客网 时间:2024/05/21 17:19

TrickGCD

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


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<=b[i]<=a[i],且b序列的gcd>=2。问你方案数。

容易想到的就是我们枚举整个序列的gcd,然后a[i]/gcd就是i位置能够填的数的个数,然后每个位置累积就能得到数列为gcd时的方案数。

最后容斥一下累加就是答案。但是最大gcd可以是100000和明显这样做n2,会超时。

那么我们把a[i]/gcd的放在一起,然后用快速幂直接求出值。

#include <bits/stdc++.h>using namespace std;const int MAXN = 1e5+7;const long long mod = 1e9+7;typedef long long LL;int n;int a[MAXN];//a数组统计每个数的个数int sum[MAXN];//前缀和LL num[MAXN];//num[i]表示数列gcd为i时的的累积LL dp[MAXN];//dp[i]表示容斥完之后也就是最终结果 gcd为i时的累积LL quick_pow(int a,int b){    long long sum = 1,base = a;    while(b)    {        if(b & 1)sum = sum*base%mod;        base  = base*base%mod;        b >>= 1;    }    return sum;}int main(){    int t;    scanf("%d",&t);    int ca = 0;    while(t--)    {        int x;        scanf("%d",&n);        memset(a,0,sizeof a);        for(int i = 0; i < n; ++i)        {            scanf("%d",&x);            a[x]++;        }        memset(sum,0,sizeof sum);        for(int i = 1; i <= 100000; ++i)sum[i] = sum[i-1] + a[i];        int flag = 1;        for(int i = 2; i <= 100000; ++i)//枚举gcd        if(!flag)num[i] = 0;        else        {            num[i] = 1;            for(int j = 0; j <= 100000; j+=i)            {                int b = sum[min(j+i-1,100000)] - sum[max(j-1,0)];//注意边界                int a = j/i;                if(!a && b)//如果存在比当前i小的数,那么后面的肯定都不符合                {                    num[i] = 0;                    flag = 0;                    break;                }                num[i] = num[i]*quick_pow(a,b)%mod;            }        }        for(int i = 100000; i >= 2; --i)        {            dp[i] = num[i];            for(int j = i<<1; j <= 100000; j +=i)            {                dp[i] -= dp[j];                dp[i] = (dp[i]%mod+mod)%mod;            }        }        LL ans = 0;        for(int i = 2; i <= 100000; ++i)ans = (ans+dp[i])%mod;        printf("Case #%d: %I64d\n",++ca,ans);    }    return 0;}