HDU6053TrickGCD

来源:互联网 发布:java方法泛型 t.class 编辑:程序博客网 时间:2024/06/05 06:58

题目连接

题意

​ 给定一个长为n的数组A,求解一定条件下能构造多少个不同数组B。条件为 1BiAi 和 对于任意的 l,r(1lrn)gcd(Bl,Bl+1,...,Br)2.

分析

​ 显然gcd最小的情况必然是在l=1,r=n时取得,所以仅考虑 l=1,r=n 的情况即可。考虑枚举 gcd(B1,B2,...,Bn) 的结果。举例gcd(B1,B2,...,Bn)=2B1 对这个方案的种类贡献为 b1=B1/2 ,那么总方案数为 ni=1bi ,容斥处理掉重复计算的结果即可。接下来问题是怎么快速求取bi 。暴力尝试A数组的每个数显然时间上不可行。于是考虑枚举 bi=j ,查询存在几个数在这个范围内。赛时一直算不清复杂度,忘了这样处理的复杂度为 O(n×ln(n)) ,一直纠结于一个 O(n32) 的算法。

代码

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<vector>#include<map>#include<cmath>#include<queue>using namespace std;const int N = 100000 + 10;const int mod = 1e9 + 7;int T, n, a[N], ca[N*2], inv[N] = {0, 1}, cnt[N], vis[N];int mn, mx;long long quick_pow(long long a,long long b){    long long ret=1;    while(b){        if(b&1)            ret=(ret*a)%mod;        b>>=1;        a=(a*a)%mod;    }    return ret;}bool isSqr[N];int lowbit(int x){    return x&-x;}void prime() {    memset(isSqr, 0, sizeof(isSqr));    for(int i=2;i<=100000;i++)    {            if(cnt[i])    continue;        for(int j=i;j<=100000;j+=i) {            cnt[j]++;            if(j % (i*i) == 0)    isSqr[j] = 1;        }    }}int getnum(int k){    long long ret=1;    for(int i=1;i<=100000;++i){        if(k*i>mx)            break;        int num=ca[k*(i+1)-1]-ca[k*i-1];        if(num==0)            continue;        ret*=quick_pow(i,num);        if(ret>mod)            ret%=mod;    }    return ret;}int main(){    for(int i=2;i<N;i++)        inv[i] = (mod - mod/i) *1ll* inv[mod%i] % mod;    prime();    scanf("%d", &T);    for(int ica=1;ica<=T;ica++)    {        scanf("%d", &n);        memset(ca,0,sizeof(ca));        mn=100000,mx=0;        for(int i=1;i<=n;i++)        {            scanf("%d", &a[i]);            mn = min(mn, a[i]);            mx = max(mx,a[i]);            ca[a[i]]++;        }        if(mn == 1)    {            printf("Case #%d: 0\n", ica);            continue;        }        for(int i=2;i<N*2;++i)            ca[i]+=ca[i-1];        int ans = 0;        int func = 1;        for(int i=2;i<=mn;i++)        {            func=getnum(i);            if(isSqr[i])    continue;            if(cnt[i] % 2)    ans += func;            else    ans -= func;            ans%=mod;        }        ans=(ans+mod)%mod;        printf("Case #%d: %d\n", ica, ans);    }}