hdu 5628 Clarke and math Dirichlet卷积

设f,g为两个数论函数,那么规定 (f∗g)(n)=∑d|n f(d)g(n/d)为f,g的Dirichlet卷积

该运算满足 交换律、结合律、分配律。

并且存在一个幺元e,使得e*f = f。   当i = 1 时 e[i] = 1,i != 1 时 e[i] = 0;


另函数 h[i]  恒等于 1.

g(i)=i1ii2i1i3i2ikik1f(ik)   =  i1ii2i1i3i2ikik1f(ik) *h(ik) .

化为Dirichlet卷积形式,g(i) =  (f*h^k)(i)

由于满足交换律, 先利用快速幂h^k。最后再拿h^k的结果与f卷积(注意,两个函数卷积的结果是函数,而不是值)

每次Dirichlet卷积的 复杂度为 O(n*logn),进行logk次卷积计算,单组复杂度O(n*logn*logk).

/*************************************************************************
 > File Name: hdu5628.cpp
 > Author: TechMonster
 > Mail: 928221136@qq.com
 > Created Time: 三  7/13 16:20:40 2016
 ************************************************************************/

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
#define MS(x,y) memset(x,y,sizeof(x))
typedef long long LL;
const int N = 100010;
const LL M = 1e9+7;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) {a += b;if(a > M) a%= M;}

int n,k;
LL f[N],ans[N],tem[N],x[N];
void Dirichlet(LL *ans, LL *x)//Dirichlet卷积
{
	MS(tem,0);
	for(int i = 1; i*i <= n; ++i)
	{
		gadd(tem[i*i],ans[i]*x[i]%M);
		for(int j = i+1; j*i <= n; ++j)
		{
			gadd(tem[i*j],ans[i]*x[j]%M);
			gadd(tem[i*j],ans[j]*x[i]%M);
		}
	}
	for(int i = 1; i <= n; ++i) ans[i] = tem[i];
}
void calc()
{
	while(k) //快速幂
	{
		if(k&1) Dirichlet(ans,x);
		Dirichlet(x,x);
		k>>=1;
	}
	Dirichlet(ans,f); //乘f
}
void solve()
{
	scanf("%d%d",&n,&k);
	for(int i = 1; i <= n; ++i)
	{
		scanf("%lld",&f[i]);
		ans[i] = 0; x[i] = 1;//初始ans为幺元,x为恒等于1的函数
	}
	ans[1] = 1;
	calc();
	for(int i = 1; i < n; ++i) printf("%lld ",ans[i]);
	printf("%lld\n",ans[n]);
}
int main()
{
	int T;scanf("%d",&T);while(T--)solve();
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值