HDU 5628 Clarke and math (Dirichlet卷積)

題目鏈接:
HDU 5628

題意:
給你f(i)(i=1,2,,n)
g(i)=i1ii2i1i3i2ikik1f(ik) mod 1000000007(1in)

題解:

h[i] 恆等於 1.

g(i)=i1|ii2|i1i3|i2ik|ik1f(ik) i1|ii2|i1i3|i2ik|ik1f(ik)h(ik).

化爲 Dirichlet 卷積形式,g(i)=(fhk)(i)=d|if(i)hk(id)
根據 狄利克雷卷積 滿足交換律和結合律, 先利用快速冪 hk 。最後再拿hk 的結果與f 卷積(注意,兩個函數卷積的結果是函數,而不是值)。
每次 Dirichlet 卷積的 複雜度爲 O(nlogn), 進行 logk 次卷積計算,總複雜度O(nlognlogk)


嗯,感覺有點難解釋。

就是兩個函數 fg 的狄利克雷卷積定義爲:(fg)(n)=j|nf(j)g(nj)

可以寫成這樣:(fg)(n)=i×j=nf(j)g(i)

然後可以發現原式的一層就是一次 f 函數和 1 函數的狄利克雷卷積。

又因爲狄利克雷卷積滿足交換律和結合律,所以g=f1k (其中1 函數滿足 1(x)=1 )。

然後快速冪套一個狄利克雷卷積就可以了。

總時間複雜度就是O(nlognlogk)


但是剛纔又想了一下。
發現了一個nlogn 的做法。

我們可以考慮 f(j ) 對 g(i) 的貢獻。

可以發現,g(i)=j|iH(ij)f(j) ,其中, H(i) 表示把 i 分成 k 個有序數的乘積的方案數。

然後我們發現 H(i) 居然是積性的。

H(pr)=(k+r1r) ,可以用線性篩預處理一下。

最後對於每一個 f(j) 枚舉 j 的倍數 k ,對 g(k×j) 產生貢獻。

總時間複雜度就是O(nlogn)

AC代碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod =1e9+7;
int n,k;
ll f[100010];
ll ans[100010];
ll tmp[100010],x[100010];
void dirichlet(ll *ans, ll *x){

    memset(tmp,0,sizeof(tmp));
    for(int i=1;i*i<=n;i++)
    {
        tmp[i*i] += ans[i]*x[i]%mod; if(tmp[i*i]>=mod) tmp[i*i]%=mod;
        for(int j=i+1;i*j<=n;j++)
        {
            tmp[i*j] += ans[i]*x[j]%mod; if(tmp[i*j]>=mod) tmp[i*j]%=mod;
            tmp[i*j] += ans[j]*x[i]%mod; if(tmp[i*j]>=mod) tmp[i*j]%=mod;   
        } 
    }
    for(int i=1;i<=n;i++)
    {
        ans[i] = tmp[i];
    }
}

void qpower(){

    while(k)
    {
        if(k&1) dirichlet(ans,x);
        k>>=1;
        dirichlet(x,x); 
    }
    dirichlet(ans,f);//乘 f 
} 
void solve()
{
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&f[i]);
        ans[i] = 0;
        x[i] = 1;
    }
    ans[1] = 1;
    qpower();

}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&k); 
        solve();
        for(int i=1;i<n;i++){
            printf("%lld ",ans[i]);
        }
        printf("%lld\n",ans[n]);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章