魔法石

前置知识

狄利克雷卷积

在学习 杜教筛 的时候,想必已经接触到了这个东西。

( 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)

其中 ( f ∗ g ) (f*g) (fg) 表示 f f f g g g 的狄利克雷卷积。

说白了,普通卷积是下标相加,狄利克雷是下标相乘。

它有几个著名的性质:

  • 计算一次的时间复杂度是 O ( n ln ⁡ n ) \mathcal O(n\ln n) O(nlnn) 。其中 ln ⁡ n \ln n lnn 来自于著名的调和级数 ∑ x = 1 n x − 1 \sum_{x=1}^{n}x^{-1} x=1nx1
  • 满足交换律、结合律——就像普通卷积一样。毕竟乘法是拥有交换律、结合律的。
常函数

就是 I ( x ) = 1 I(x)=1 I(x)=1 ,没什么好说的。

题目

题目描述
给定 f ( 1 ) , f ( 2 ) , f ( 3 ) , … , f ( n ) f(1),f(2),f(3),\dots,f(n) f(1),f(2),f(3),,f(n) k k k ,对于 ∀ x ∈ [ 1 , n ] \forall x\in[1,n] x[1,n] ,求

g ( x ) = ∑ x 1 ∣ x ∑ x 2 ∣ x 1 ∑ x 3 ∣ x 2 ⋯ ∑ x k ∣ x k − 1 f ( x k ) g(x)=\sum_{x_1|x}\sum_{x_2|x_1}\sum_{x_3|x_2}\dots\sum_{x_k|x_{k-1}}f(x_k) g(x)=x1xx2x1x3x2xkxk1f(xk)

输出答案除以 ( 1 0 9 + 7 ) (10^9+7) (109+7) 的余数。

数据范围与约定
对于 30 % 30\% 30% 的数据, n ≤ 100 , k ≤ 5 n ≤ 100, k ≤ 5 n100,k5

对于 100 % 100\% 100% 的数据, n ≤ 100000 , k ≤ 100000 , T ≤ 5 n ≤ 100000, k ≤ 100000, T ≤ 5 n100000,k100000,T5

思路

暴力

对于 30 % 30\% 30% 的数据,约数的数量并不多,层数也不多, d f s \tt{dfs} dfs 即可。

动态规划

发现有重复计算的情况。于是考虑记忆化搜索。也就是动态规划。

g i ( x ) = ∑ x 1 ∣ x ∑ x 2 ∣ x 1 ∑ x 3 ∣ x 2 ⋯ ∑ x i ∣ x i − 1 f ( x i ) g_{i}(x)=\sum_{x_1|x}\sum_{x_2|x_1}\sum_{x_3|x_2}\dots\sum_{x_i|x_{i-1}}f(x_i) gi(x)=x1xx2x1x3x2xixi1f(xi)

那么一定有 g i ( x ) = ∑ d ∣ x g i − 1 ( d ) g_{i}(x)=\sum_{d|x}g_{i-1}(d) gi(x)=dxgi1(d)

答案就是 g k ( x ) g_{k}(x) gk(x) ,所以递推即可。可惜跟暴力分差不了多少。

卷积优化

然而这玩意儿……长得跟 狄利克雷卷积 很像!

我们把 I I I 用进去,发现——

g i ( x ) = ∑ d ∣ x g i − 1 ( d ) ⋅ I ( x d ) = ( g i − 1 ∗ I ) ( x ) g_{i}(x)=\sum_{d|x}g_{i-1}(d)\cdot I\left(\frac{x}{d}\right)=(g_{i-1}*I)(x) gi(x)=dxgi1(d)I(dx)=(gi1I)(x)

没错!这就是狄利克雷卷积的形式!

结合边界条件 g 0 ( x ) = f ( x ) g_{0}(x)=f(x) g0(x)=f(x)

得到这样一个式子 g i = { [ ( f ∗ I ) ∗ I ] ∗ I } ∗ ⋯ ∗ I g_{i}=\{[(f*I)*I]*I\}*\cdots*I gi={[(fI)I]I}I

但是,狄利克雷卷积是拥有 结合律 的!

所以,我们把它简写一下: g k = f ∗ I k g_k=f*I^k gk=fIk

拥有结合律的幂,是可以使用 快速幂 的。所以复杂度就只有 O ( n log ⁡ n log ⁡ k ) \mathcal O(n\log n\log k) O(nlognlogk) 了。

代码

如果你不知道狄利克雷卷积应该怎么写(自然就搞不清楚复杂度),我贴一份代码,仅供参考。

void multiply(int a[],int b[],int c[]){
	/* a*b的值存在c里面 */
	memset(c,0,sizeof c);
	for(int i=1; i<=n; ++i)
		for(int j=1; j<=n/i; ++j)
			c[i*j] += a[i]*b[j];
}

于是其复杂度就是 n + n 2 + n 3 + ⋯ + n n = O ( n ln ⁡ n ) n+\frac{n}{2}+\frac{n}{3}+\cdots+\frac{n}{n}=\mathcal O(n\ln n) n+2n+3n++nn=O(nlnn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值