HDU6053 TrickGCD

題目連接

題意

​ 給定一個長爲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);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章