HDU 5628 Clarke and math Dirichlet卷积+快速幂
题意:bc round 72 中文题面
分析(官方题解):
如果学过Dirichlet卷积的话知道这玩意就是g(n)=(f*1^k)(n),
由于有结合律,所以我们快速幂一下1^k就行了。
当然,强行正面刚和式也是能搞的(反正我不会)。
一次Dirichlet卷积复杂度是O(nlogn)的,所以总时间复杂度为O(nlognlogk).
注:普及Dirichlet卷积概念,设f,g为两个数论函数,
那么规定 (f∗g)=∑d|nf(d)g(n/d)为f,g的Dirichlet卷积
Dirichlet卷积有以下性质,有交换律和结合律,对加法有分配律
f∗(g∗h)=(f∗g)∗h
f∗(g+h)=f∗g+f∗h
f∗g=g∗f
然后对于这个题解上所说的一次Dirichlet卷积是O(nlogn)这个我不是很懂
但是看网上大神写的估计也差不多,感觉和埃氏筛的复杂度差不多甚至还要小应该是在O(nlognlogn)左右
所以下面的代码的复杂度大概应该是O(nlog^2nlogk)
然后顺带提一下,对于Dirichlet卷积来说,规定有一个幺元e的话
即:f*e=f,
这个幺元函数有一个性质e[1]=1,e[k]=0,k=1,2,3.....
这个在快速幂的时候会用到
然后上代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> using namespace std; typedef long long LL; const int N=1e5+5; const LL mod=1e9+7; int n,k; LL f[N],s[N],ans[N],tmp[N]; void solve() { while(k) { if(k&1) { for(int i=1; i<=n; ++i)tmp[i]=0; for(int i=1; i*i<=n; ++i) { tmp[i*i]=(tmp[i*i]+ans[i]*s[i])%mod; for(int j=i+1; j*i<=n; ++j) tmp[i*j]+=(ans[i]*s[j]%mod+ans[j]*s[i]%mod)%mod,tmp[i*j]%=mod; } for(int i=1; i<=n; ++i)ans[i]=tmp[i]; } for(int i=1; i<=n; ++i)tmp[i]=0; for(int i=1; i*i<=n; ++i) { tmp[i*i]=(tmp[i*i]+s[i]*s[i])%mod; for(int j=i+1; j*i<=n; ++j) tmp[i*j]+=(s[i]*s[j]%mod+s[j]*s[i]%mod)%mod,tmp[i*j]%=mod; } for(int i=1;i<=n;++i)s[i]=tmp[i]; k>>=1; } for(int i=1; i<=n; ++i)tmp[i]=0; for(int i=1; i*i<=n; ++i) { tmp[i*i]=(tmp[i*i]+f[i]*ans[i])%mod; for(int j=i+1; j*i<=n; ++j) tmp[i*j]+=(f[i]*ans[j]%mod+f[j]*ans[i]%mod)%mod,tmp[i*j]%=mod; } for(int i=1;i<=n;++i)ans[i]=tmp[i]; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&k); for(int i=1; i<=n; ++i) scanf("%I64d",&f[i]),s[i] = 1,ans[i] = 0; ans[1] = 1; solve(); for(int i=1; i<n; ++i) printf("%I64d ",ans[i]); printf("%I64d\n",ans[n]); } return 0; }