【HDU5628】Clarke and math-狄利克雷卷积+快速幂

来源:互联网 发布:mysql和oracle的比较 编辑:程序博客网 时间:2024/06/08 09:33

测试地址:Clarke and math
题目大意:fg是定义在集合{1,2,...,n}的一个函数,且g(i)=i1|ii2|i1...ik|ik1f(ik),给定kf(1),f(2),...f(n),求g(1),g(2),...,g(n)
做法:此题需要用到狄利克雷卷积+快速幂。
定义两个算术函数fg的狄利克雷卷积为:(fg)(n)=d|nf(d)g(n/d),那么函数的狄利克雷卷积满足以下性质:
交换律:fg=gf
结合律:f(gh)=(fg)h
分配律:f(g+h)=fg+fh
那么再看我们要求的函数g,我们一层一层展开,设函数I(n)=1,则:
f(ik1)=ik|ik1f(ik)=ik|ik1f(ik)I(ik1/ik),即f=If
f′′(ik2)=ik1|ik2f(ik1)=ik1|ik2f(ik1)I(ik2/ik1),即f′′=If=IIf
...
这样一直推导下去,得到g=III...IfkI),由于狄利克雷卷积满足结合律,所以可以用快速幂加速计算。在计算狄利克雷卷积的时候,如果对于每一个1in都按照定义枚举约数计算贡献的话,时间复杂度显然爆炸,所以我们可以枚举约数,然后枚举有哪些i需要加上这个约数的贡献即可,那么计算一次狄利克雷卷积的复杂度为O(nlogn),总的时间复杂度就是O(nlognlogk)
注意如果用结构体传参会爆栈,直接用数组+指针传参就可以了。以及这题的格式要求行末不能有空格,但要有换行,注意一下就行了。
狄利克雷卷积的作用当然不只是做这种题,它为一堆有关莫比乌斯反演和杜教筛的式子提供了简明的记号,这个后面有时间再进行小结,这里就不再赘述了。
以下是本人代码:

#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>#define mod 1000000007#define ll long longusing namespace std;int T,n,k;ll f[100010],g[100010],s[100010],ss[100010],pro[100010];void mult(ll *a,ll *b){  memset(s,0,sizeof(s));  for(int i=1;i*i<=n;i++)  {    s[i*i]=(s[i*i]+a[i]*b[i])%mod;    for(int j=i+1;i*j<=n;j++)      s[i*j]=(s[i*j]+a[i]*b[j]+a[j]*b[i])%mod;  }  for(int i=1;i<=n;i++) a[i]=s[i];}void power(ll *a,int p){  for(int i=1;i<=n;i++) ss[i]=a[i];  memset(pro,0,sizeof(pro));  pro[1]=1;  while(p)  {    if (p&1) mult(pro,ss);    mult(ss,ss);p>>=1;  }  for(int i=1;i<=n;i++) a[i]=pro[i];}int main(){  scanf("%d",&T);  while(T--)  {    scanf("%d%d",&n,&k);    for(int i=1;i<=n;i++)    {      scanf("%lld",&f[i]);      g[i]=1;    }    power(g,k);    mult(f,g);    for(int i=1;i<=n;i++)      printf("%lld%c",f[i],(i==n)?'\n':' ');  }  return 0;}
原创粉丝点击