【HDU 5628】Clarke and math

传送门


problem

给出 n , k n,k n,k 和数论函数 f ( i ) , i ∈ [ 1 , n ] f(i),i\in[1,n] f(i),i[1,n]。对于 ∀ i ∈ [ 1 , n ] \forall i\in[1,n] i[1,n],请求出:

g ( i ) = ∑ i 1 ∣ i ∑ i 2 ∣ i 1 ∑ i 3 ∣ i 2 ⋯ ∑ i k ∣ i k − 1 f ( i k ) g(i)=\sum_{i_1|i}\sum_{i_2|i_1}\sum_{i_3|i_2}\cdots\sum_{i_{k}|i_{k-1}}f(i_k) g(i)=i1ii2i1i3i2ikik1f(ik)

答案对 1 0 9 + 7 10^9+7 109+7 取模。

数据范围: 1 ≤ n , k ≤ 1 0 5 1\le n,k\le 10^5 1n,k105 0 ≤ f ( i ) &lt; 1 0 9 + 7 0\le f(i)&lt;10^9+7 0f(i)<109+7


solution

先介绍一下狄利克雷卷积,对于两个数论函数 f ( n ) f(n) f(n) g ( n ) g(n) g(n),有:

( f ∗ g ) ( n ) = ∑ d ∣ n f ( d ) g ( n d ) (f*g)(n)=\sum_{d|n}f(d)g(\frac n d) (fg)(n)=dnf(d)g(dn)

求卷积的话,我们可以枚举约数 d d d,然后在倍数的地方加上相应的贡献,复杂度是 O ( n log ⁡ n ) O(n\log n) O(nlogn) 的。

然后回到这道题来。

k = 1 k=1 k=1 的时候,有:

g 1 ( i ) = ∑ i 1 ∣ i f ( i 1 ) = ∑ i 1 ∣ i f ( i 1 ) 1 ( i i 1 ) = f ∗ 1 g_1(i)=\sum_{i_1|i}f(i_1)=\sum_{i_1|i}f(i_1)1(\frac{i}{i_1})=f*1 g1(i)=i1if(i1)=i1if(i1)1(i1i)=f1

其中 ∀ i ∈ N ∗ , 1 ( i ) = 1 \forall i\in N^*,1(i)=1 iN,1(i)=1

k = 2 k=2 k=2 时,有:

g 2 ( i ) = ∑ i 1 ∣ i g 1 ( i 1 ) = ∑ i 1 ∣ i g 1 ( i 1 ) 1 ( i i 1 ) = g 1 ∗ 1 = f ∗ 1 ∗ 1 g_2(i)=\sum_{i_1|i}g_1(i_1)=\sum_{i_1|i}g_1(i_1)1(\frac{i}{i_1})=g_1*1=f*1*1 g2(i)=i1ig1(i1)=i1ig1(i1)1(i1i)=g11=f11

那么不断递推,发现最终的答案就是 f ∗ 1 ∗ 1 ∗ ⋯ ∗ 1 f*1*1*\cdots*1 f111(就是 f f f k k k 1 1 1 卷起来)。

那么可以用快速幂优化这一过程,时间复杂度 O ( n log ⁡ 2 n ) 。 O(n\log^2n)。 O(nlog2n)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define P 1000000007
using namespace std;
int n,k,F[N],I[N],ans[N],temp[N];
int add(int x,int y)  {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y)  {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y)  {return 1ll*x*y%P;}
void Dirichlet(int *f,int *g){
	memset(temp,0,sizeof(temp));
	for(int i=1;i*i<=n;++i){
		temp[i*i]=add(temp[i*i],mul(f[i],g[i]));
		for(int j=i+1;i*j<=n;++j)
			temp[i*j]=add(temp[i*j],add(mul(f[i],g[j]),mul(f[j],g[i])));
	}
	memcpy(f,temp,sizeof(temp));
}
void Solve(){
	for(int i=1;i<=n;++i)  I[i]=1,ans[i]=0;
	ans[1]=1;
	for(;k;k>>=1,Dirichlet(I,I))
		if(k&1)  Dirichlet(ans,I);
	Dirichlet(ans,F);
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&k);
		for(int i=1;i<=n;++i)  scanf("%d",&F[i]);
		Solve();
		for(int i=1;i<=n;++i)  printf("%d%c",ans[i],i==n?'\n':' ');
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值