函数与表达式的反演变换

函数式编程的演变与实现 本文深入探讨了函数式编程的核心概念,包括列表、函数表达式、闭包和单子等。从Lisp的列表和动态作用域开始,再到Scheme和Haskell等语言中的尾递归优化和惰性求值,揭示了函数式编程语言在处理状态和数据流方面的独特优势。文章还探讨了如何在函数式编程中实现数据结构的原地更新,以及如何通过单子概念融入状态管理。 阅读详情

下面提到的所有知识不一定同属一个范畴,但是它们的思想和作用相似,所以将其归为一篇文章。


莫比乌斯反演

请先学习狄利克雷卷积,否则您会一脸懵逼。

一般式:
F ( n ) = ∑ d ∣ n G ( d ) → G ( n ) = ∑ d ∣ n μ ( n d ) F ( d ) F ( n ) = ∑ n ∣ d G ( d ) → G ( n ) = ∑ n ∣ d μ ( d n ) F ( d ) \begin{aligned} F(n) = \sum_{d | n} G(d) \rightarrow G(n) = \sum_{d | n} \mu(\frac{n}{d}) F(d) \\ F(n) = \sum_{n | d} G(d) \rightarrow G(n) = \sum_{n | d} \mu(\frac{d}{n}) F(d) \end{aligned} F(n)=d∣n∑​G(d)→G(n)=d∣n∑​μ(dn​)F(d)F(n)=n∣d∑​G(d)→G(n)=n∣d∑​μ(nd​)F(d)​

证明可以考虑下面恒等式的第一条。

然后再放一些有助于反演的恒等式:(其中 ∗ * ∗ 指狄利克雷卷积, ϵ ( n ) \epsilon(n) ϵ(n) 表示 [ n = 1 ] [n = 1] [n=1])
μ ∗ 1 = ϵ φ ∗ 1 = i d μ ∗ i d = μ ∗ 1 ∗ φ = ϵ ∗ φ = φ φ ( n ) = ∑ i = 1 n ϵ ( gcd ⁡ ( n , i ) ) φ ( n m ) = φ ( n ) φ ( m ) gcd ⁡ ( n , m ) φ ( gcd ⁡ ( n , m ) ) ∑ i = 1 n ∑ j = 1 n ϵ ( gcd ⁡ ( i , j ) ) = 2 ⋅ ( ∑ i = 1 n φ ( i ) ) − 1 ∑ d ∣ i j 1 = ∑ x ∣ i ∑ y ∣ j ϵ ( gcd ⁡ ( x , y ) ) ∑ d ∣ i j k 1 = ∑ x ∣ i ∑ y ∣ j ∑ z ∣ k ϵ ( gcd ⁡ ( x , y ) ) ϵ ( gcd ⁡ ( y , z ) ) ϵ ( gcd ⁡ ( x , z ) ) ⋯ ⋯ \begin{aligned} &\mu * 1 = \epsilon \\ & \varphi * 1 = id \\ & \mu * id = \mu * 1 * \varphi = \epsilon * \varphi = \varphi \\ &\varphi(n) = \sum_{i = 1}^n \epsilon(\gcd(n, i)) \\ &\varphi(nm) = \frac{\varphi(n)\varphi(m)\gcd(n, m)}{\varphi(\gcd(n, m))} \\ &\sum_{i=1}^n\sum_{j = 1}^n \epsilon(\gcd(i, j)) = 2 \cdot (\sum_{i = 1}^n\varphi(i)) - 1 \\ & \sum_{d | ij} 1 = \sum_{x|i} \sum_{y|j} \epsilon(\gcd(x, y)) \\ & \sum_{d | ijk} 1 = \sum_{x|i} \sum_{y|j} \sum_{z|k} \epsilon(\gcd(x, y))\epsilon(\gcd(y, z))\epsilon(\gcd(x, z)) \\ &\cdots\cdots \end{aligned} ​μ∗1=ϵφ∗1=idμ∗id=μ∗1∗φ=ϵ∗φ=φφ(n)=i=1∑n​ϵ(gcd(n,i))φ(nm)=φ(gcd(n,m))φ(n)φ(m)gcd(n,m)​i=1∑n​j=1∑n​ϵ(gcd(i,j))=2⋅(i=1∑n​φ(i))−1d∣ij∑​1=x∣i∑​y∣j∑​ϵ(gcd(x,y))d∣ijk∑​1=x∣i∑​y∣j∑​z∣k∑​ϵ(gcd(x,y))ϵ(gcd(y,z))ϵ(gcd(x,z))⋯⋯​

推完式子之后,可以考虑用一些数论筛法预处理某些项,或是用整除分块求值。


拿一道例题来讲解吧。

一共 T T T 组询问 ( T ≤ 1 0 4 ) (T \le 10^4) (T≤104),给定常数 K K K,每次询问单独给定 N N N,满足 N ≤ 1 0 7 N \le 10^7 N≤107,求出:
∑ i = 1 N ∑ j = 1 N ( i + j ) K gcd ⁡ ( i , j ) μ 2 ( gcd ⁡ ( i , j ) )   (   m o d   2 32 ) \sum_{i = 1}^N\sum_{j = 1}^N(i + j)^K \gcd(i, j)\mu^2(\gcd(i, j))~(\bmod 2^{32}) i=1∑N​j=1∑N​(i+j)Kgcd(i,j)μ2(gcd(i,j)) (mod232)
看模数 2 32 2^{32} 232,显然可以自然溢出,这可以帮助我们减小常数。

接下来表演推式子:
原 式 = ∑ d = 1 N ∑ i = 1 N ∑ j = 1 N [ gcd ⁡ ( i , j ) = d ] ( ( i + j ) K d μ 2 ( d ) ) = ∑ d = 1 N d K + 1 μ 2 ( d ) ∑ i = 1 ⌊ N d ⌋ ∑ j = 1 ⌊ N d ⌋ ϵ ( gcd ⁡ ( i , j ) ) ( i + j ) K = ∑ d = 1 N d K + 1 μ 2 ( d ) ∑ i = 1 ⌊ N d ⌋ ∑ j = 1 ⌊ N d ⌋ ( i + j ) K ∑ x ∣ gcd ⁡ ( i , j ) μ ( x ) = ∑ d = 1 N d K + 1 μ 2 ( d ) ∑ x = 1 ⌊ N d ⌋ x K μ ( x ) ∑ i = 1 ⌊ N d x ⌋ ∑ j = 1 ⌊ N d x ⌋ ( i + j ) K = ∑ T = 1 N T K ∑ i = 1 ⌊ N T ⌋ ∑ j = 1 ⌊ N T ⌋ ( i + j ) K ∑ d ∣ T d μ 2 ( d ) μ ( T d ) \begin{aligned} 原式 &= \sum_{d = 1}^N\sum_{i = 1}^N\sum_{j = 1}^N[\gcd(i, j) = d]((i + j)^K d\mu^2(d)) \\ &= \sum_{d = 1}^Nd^{K + 1}\mu^2(d)\sum_{i = 1}^{\lfloor\frac{N}{d}\rfloor} \sum_{j = 1}^{\lfloor\frac{N}{d}\rfloor} \epsilon(\gcd(i, j))(i + j)^K \\ &= \sum_{d = 1}^N d^{K + 1}\mu^2(d)\sum_{i = 1}^{\lfloor\frac{N}{d}\rfloor}\sum_{j = 1}^{\lfloor\frac{N}{d}\rfloor}(i + j)^K\sum_{x | \gcd(i, j)} \mu(x) \\ &= \sum_{d = 1}^Nd^{K + 1}\mu^2(d)\sum_{x = 1}^{\lfloor\frac{N}{d}\rfloor}x^K\mu(x)\sum_{i = 1}^{\lfloor\frac{N}{dx}\rfloor}\sum_{j = 1}^{\lfloor\frac{N}{dx}\rfloor}(i + j)^K \\ &= \sum_{T = 1}^NT^K\sum_{i = 1}^{\lfloor\frac{N}{T}\rfloor} \sum_{j = 1}^{\lfloor\frac{N}{T}\rfloor}(i + j)^K \sum_{d|T}d\mu^2(d)\mu(\frac{T}{d}) \end{aligned} 原式​=d=1∑N​i=1∑N​j=1∑N​[gcd(i,j)=d]((i+j)Kdμ2(d))=d=1∑N​dK+1μ2(d)i=1∑⌊dN​⌋​j=1∑⌊dN​⌋​ϵ(gcd(i,j))(i+j)K=d=1∑N​dK+1μ2(d)i=1∑⌊dN​⌋​j=1∑⌊dN​⌋​(i+j)Kx∣gcd(i,j)∑​μ(x)=d=1∑N​dK+1μ2(d)x=1∑⌊dN​⌋​xKμ(x)i=1∑⌊dxN​⌋​j=1∑⌊dxN​⌋​(i+j)K=T=1∑N​TKi=1∑⌊TN​⌋​j=1∑⌊TN​⌋​(i+j)Kd∣T∑​dμ2(d)μ(dT​)​
到这里式子就是可做的了。


我猜你现在肯定一脸懵。

那就先来讲一讲吧。

  • S t e p   1 Step~1 Step 1

我们考虑枚举这个 gcd ⁡ \gcd gcd 的值。

为什么这里要做这样一个看似只会添一层循环的无意义举动呢?

可以发现,在后面的式子中多出了一个艾弗森括号,这个括号里写下的等式是可以转化为单位元的形式的。

  • S t e p   2 Step~2 Step 2

我们考虑对于每一个 d d d,只枚举 d d d 的倍数。

(实际上,此时枚举的 i , j i, j i,j 在后面的运算中会乘上一个 d d d。)

这样,艾弗森括号中的等式就可以被改写了,具体来说就是 [ gcd ⁡ ( i d , j d ) = d ] = [ gcd ⁡ ( i , j ) = 1 ] [\gcd(id, jd) = d] =[\gcd(i, j) = 1] [gcd(id,jd)=d]=[gcd(i,j)=1]。

在上面的式子中,后者已经写成了单位元的形式。

值得注意的是,由于 i , j i, j i,j 实际上除去了一个 d d d,所以运算中多出来的一个 d K d^K dK 要提到前面去。同时只和 d d d 有关的一项 d μ 2 ( d ) d\mu^2(d) dμ2(d) 也要提到前面去。

  • S t e p   3 Step~3 Step 3

还记得上面的公式 μ ∗ 1 = ϵ \mu * 1 = \epsilon μ∗1=ϵ 吗?我们可以直接把这个单位元转化为莫比乌斯函数和 1 1 1 的狄利克雷卷积!

所以,把卷积用好了,莫比乌斯反演就不难了,再难也只可能难在函数的筛法上。

然后呢?我们再把这个枚举的 x x x 提前,把 i , j i, j i,j 再次强制转化为 d x dx dx 的倍数,多出来的 x K x^K xK 同 μ ( x ) \mu(x) μ(x) 一起往前提。

继续,我们发现这个式子代数还是太多了,于是考虑枚举 T = d x T = dx T=dx,那么 d d d 必然是 T T T 的因数,此时的 x x x 则恰好等于 T d \frac{T}{d} dT​,这样化简出来,式子已经可做了。


问题是,我们怎样知道式子是可做的呢?

我们设 S ( N ) = ∑ i = 1 N ∑ j = 1 N ( i + j ) K S(N) = \sum_{i = 1}^N\sum_{j = 1}^N(i + j)^K S(N)=∑i=1N​∑j=1N​(i+j)K, f ( T ) = ∑ d ∣ T d μ 2 ( d ) μ ( T d ) f(T) = \sum_{d|T}d\mu^2(d)\mu(\frac{T}{d}) f(T)=∑d∣T​dμ2(d)μ(dT​)。

现在整个式子化简成了这样一个形式: ∑ T = 1 N T K S ( ⌊ N T ⌋ ) f ( T ) \sum_{T = 1}^N T^KS(\lfloor\frac{N}{T}\rfloor)f(T) ∑T=1N​TKS(⌊TN​⌋)f(T)。

接下来分别考虑线性预处理 S S S 和 f f f。

可以发现在 S ( N ) S(N) S(N) 中,从 2 ∼ 2 N 2 \sim 2N 2∼2N 的每一个数 d K d^K dK 一共被计算了 min ⁡ ( d − 1 , 2 N + 1 − d ) \min(d - 1, 2N + 1 - d) min(d−1,2N+1−d) 次。

于是我们可以再设两个辅助函数 F ( N ) = ∑ i = 1 N i K F(N) = \sum_{i = 1}^N i^K F(N)=∑i=1N​iK, G ( N ) = ∑ i = 1 N F ( i ) G(N) = \sum_{i = 1}^N F(i) G(N)=∑i=1N​F(i),显然这两个函数都是可以线性预处理的。

那么我们有式子: S ( N ) = ∑ i = N + 1 2 N F ( i ) − ∑ i = 1 N F ( i ) = G ( 2 N ) − G ( N ) S(N)=\sum_{i=N+1}^{2N}F(i)-\sum_{i = 1}^N F(i) = G(2N)-G(N) S(N)=∑i=N+12N​F(i)−∑i=1N​F(i)=G(2N)−G(N),同样也可以线性预处理。

再来看 f f f,可以发现 f f f 是一个狄利克雷卷积的标准形式,并且卷的是两个积性函数,说明整个式子也是一个积性函数。如果您阅读过我的文章《积性函数的筛法》,可以知道这样的函数是可以欧拉筛的。

我们来讨论一下 f f f 在 p k   ( p ∈ P r i m e ) p^k~(p \in Prime) pk (p∈Prime) 处的取值吧。

  • k = 0 k = 0 k=0,显然有 f ( 1 ) = 1 f(1) = 1 f(1)=1;
  • k = 1 k = 1 k=1,有 f ( p ) = μ ( p ) + p μ 2 ( p ) = p − 1 f(p) = \mu(p) + p\mu^2(p) = p - 1 f(p)=μ(p)+pμ2(p)=p−1;
  • k = 2 k = 2 k=2,有 f ( p 2 ) = μ ( p 2 ) + p μ 2 ( p ) μ ( p ) + p 2 μ ( p 2 ) = − p f(p^2) = \mu(p^2) + p\mu^2(p)\mu(p) + p^2\mu(p^2) = -p f(p2)=μ(p2)+pμ2(p)μ(p)+p2μ(p2)=−p;
  • k ≥ 3 k \ge 3 k≥3,则每一项都存在 μ \mu μ 为 0 0 0 的情况,答案为 0 0 0。

次数很好判断,所以分讨即可。

最后,可以发现 N T \frac{N}{T} TN​ 的取值个数是 N \sqrt{N} N ​ 级别的,所以可以套用整除分块。同时为了方便,可以把 T K T^K TK 这一项扔进 f f f 中一起预处理(最后求前缀和时配合 F F F 数组一起求即可)。

为了省空间,把 F F F 和 G G G 当作一个数组,毕竟有了 G G G, F F F 也没什么用了。

贴上代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>

#define MAXN 20000005

using uint = unsigned int;
using namespace std;

int T, N, K, len, Prime[MAXN];
uint f[MAXN], F[MAXN];
bool NotPrime[MAXN];

inline uint Pow(uint a, uint b)
{
	uint res = 1;
	while(b)
	{
		if(b & 1) res = res * a;
		a = a * a;
		b >>= 1;
	}
	return res;
}

inline void Sieve(int n, int k)
{
	f[1] = F[1] = 1;
	for(register int i = 2; i <= n; i++)
	{
		if(!NotPrime[i])
		{
			Prime[++len] = i;
			f[i] = i - 1;
			F[i] = Pow(i, k);
		}
		for(register int j = 1; j <= len && i * Prime[j] <= n; j++)
		{
			NotPrime[i * Prime[j]] = true;
			F[i * Prime[j]] = F[i] * F[Prime[j]];
			if(i % Prime[j] == 0)
			{
				if((i / Prime[j]) % Prime[j]) f[i * Prime[j]] = -Prime[j] * f[i / Prime[j]];
				break;
			}
			f[i * Prime[j]] = f[i] * f[Prime[j]];
		}
	}
	for(register int i = 2; i <= n; i++) f[i] = f[i - 1] + f[i] * F[i], F[i] += F[i - 1];
	for(register int i = 2; i <= n; i++) F[i] += F[i - 1];
	return;
}

inline uint Count(int k) { return F[k * 2] - F[k] * 2; }

int main()
{
	scanf("%d%d%d", &T, &N, &K), Sieve(N << 1, K);
	while(T--)
	{
		int n; scanf("%d", &n);
		uint ans = 0;
		for(register int l = 1, r; l <= n; l = r + 1)
		{
			r = n / (n / l);
			ans += Count(n / l) * (f[r] - f[l - 1]);
		}
		printf("%u\n", ans);
	}
	return 0;
}


二项式反演

其实二项式反演有一个更不广为人知的名字——广义容斥原理。

一般有两种形式:
f ( n ) = ∑ i = 0 n C n i g ( i ) ⇔ g ( n ) = ∑ i = 0 n ( − 1 ) n − i C n i f ( i ) f ( n ) = ∑ i = n m C i n g ( i ) ⇔ g ( n ) = ∑ i = n m ( − 1 ) i − n C i n f ( i ) \begin{aligned} f(n) = \sum_{i = 0}^nC_n^ig(i) \Leftrightarrow g(n) = \sum_{i = 0}^n(-1)^{n - i}C_n^if(i) \\ f(n) = \sum_{i = n}^mC_i^ng(i) \Leftrightarrow g(n) = \sum_{i = n}^m(-1)^{i - n} C_i^nf(i) \end{aligned} f(n)=i=0∑n​Cni​g(i)⇔g(n)=i=0∑n​(−1)n−iCni​f(i)f(n)=i=n∑m​Cin​g(i)⇔g(n)=i=n∑m​(−1)i−nCin​f(i)​
正如前面所说,我们一般设 g ( n ) g(n) g(n) 为 “恰好满足 n n n 个性质” 的方案数。对于形式一和形式二,我们一般设 f ( n ) f(n) f(n) 为 “至多满足 n n n 个性质” 和 “至少满足 n n n 个性质” 的方案数。

显然 f ( n ) f(n) f(n) 更好求,并且 g ( n ) g(n) g(n) 一般都做为题目要求的东西。

例题可以康康这道,太板了就不放题解和代码了。


单位根反演

其实就是这样一个式子: [ n ∣ k ] = 1 n ∑ i = 0 n − 1 ω n i k [n | k] = \frac{1}{n}\sum_{i = 0}^{n - 1} \omega_n^{ik} [n∣k]=n1​∑i=0n−1​ωnik​。

ω \omega ω 指单位根。从数学上讲,它是一个复数。但是从广义上讲,只要在给定的数域和运算内满足 ω n n \omega^n_n ωnn​(即 n n n 阶单位根的 n n n 次方)为 1 1 1,那么它就可以称做单位根。所以 ω \omega ω 实际上还可以是原根。

证明略去~~,显然是因为不会~~。


康康例题

首先我们有 ⌊ i k ⌋ = ( ∑ j = 0 i [ k ∣ j ] ) − 1 \lfloor\frac{i}{k}\rfloor = (\sum_{j = 0}^i[k | j]) - 1 ⌊ki​⌋=(∑j=0i​[k∣j])−1,这样就转化为单位根反演的形式了。

(可以不管后面的减 1 1 1,因为可以直接拆开然后二项式定理搞掉。)

然后我们直接莽式子:
∑ i = 0 n C n i p i ( ( ∑ j = 0 i [ k ∣ j ] ) − 1 ) = ( ∑ i = 0 n C n i p i ∑ j = 0 i 1 k ∑ t = 0 k − 1 ω k j t ) − ( p + 1 ) n = 1 k ( ∑ t = 0 k − 1 ∑ i = 0 n C n i p i ∑ j = 0 i ( ω k t ) j ) − ( p + 1 ) n = 1 k ( ∑ t = 0 k − 1 ∑ i = 0 n C n i p i ω k t ( i + 1 ) − 1 ω k t − 1 ) − ( p + 1 ) n = 1 k ( ∑ t = 0 k − 1 ( ∑ i = 0 n C n i p i ω k t ( i + 1 ) ) − ( p + 1 ) n ω k t − 1 ) − ( p + 1 ) n = 1 k ( ∑ t = 0 k − 1 ( ω k t ( p ω k t + 1 ) n − ( p + 1 ) n ω k t − 1 ) − ( p + 1 ) n \begin{aligned} &\sum_{i = 0}^n C_n^i p^i ((\sum_{j = 0}^i [k | j]) - 1) \\ =&(\sum_{i = 0}^nC_n^i p^i\sum_{j = 0}^i\frac{1}{k}\sum_{t = 0}^{k - 1}\omega_k^{jt}) - (p + 1)^n \\ =&\frac{1}{k}(\sum_{t = 0}^{k - 1}\sum_{i = 0}^n C_n^i p^i\sum_{j = 0}^i(\omega_k^t)^j) - (p + 1)^n \\ =&\frac{1}{k}(\sum_{t = 0}^{k - 1}\sum_{i = 0}^n C_n^i p^i\frac{\omega_k^{t(i + 1)} - 1}{\omega_k^t - 1}) - (p + 1)^n \\ =&\frac{1}{k}(\sum_{t = 0}^{k - 1}\frac{(\sum_{i = 0}^n C_n^i p^i \omega_k^{t(i + 1)}) - (p + 1)^n}{\omega_k^t - 1}) - (p + 1)^n \\ =&\frac{1}{k}(\sum_{t = 0}^{k - 1}\frac{(\omega_k^t(p\omega_k^t + 1)^n- (p + 1)^n}{\omega_k^t - 1}) - (p + 1)^n \end{aligned} =====​i=0∑n​Cni​pi((j=0∑i​[k∣j])−1)(i=0∑n​Cni​pij=0∑i​k1​t=0∑k−1​ωkjt​)−(p+1)nk1​(t=0∑k−1​i=0∑n​Cni​pij=0∑i​(ωkt​)j)−(p+1)nk1​(t=0∑k−1​i=0∑n​Cni​piωkt​−1ωkt(i+1)​−1​)−(p+1)nk1​(t=0∑k−1​ωkt​−1(∑i=0n​Cni​piωkt(i+1)​)−(p+1)n​)−(p+1)nk1​(t=0∑k−1​ωkt​−1(ωkt​(pωkt​+1)n−(p+1)n​)−(p+1)n​
可以发现,上面的式子中大量地用到了二项式定理,这玩意确实会经常和单位根反演配合使用。

上面的式子已经可做了,时间复杂度为 O ( k log ⁡ 2 n ) O(k \log_2 n) O(klog2​n),带 log ⁡ \log log 是因为快速幂。

然后您会发现您连样例都过不去。

我们再来审视一下整个式子。可以发现,当 t = 0 t = 0 t=0 时, ω k t = 1 \omega_k^t = 1 ωkt​=1,此时分母为 0 0 0,这一段贡献是算错了的。

我们回到原来的式子: ∑ i = 0 n C n i p i ∑ j = 0 i ( ω k t ) j \sum_{i = 0}^n C_n^i p^i\sum_{j = 0}^i(\omega_k^t)^j ∑i=0n​Cni​pi∑j=0i​(ωkt​)j,当 ω k t = 1 \omega_k^t = 1 ωkt​=1 时,该式子为 ∑ i = 0 n C n i p i ( i + 1 ) \sum_{i = 0}^n C_n^i p^i(i + 1) ∑i=0n​Cni​pi(i+1)。

我们考虑一下这个式子怎么做。

首先我们把后面的加 1 1 1 提出来,直接做一遍二项式定理。

剩下的就是 ∑ i = 0 n C n i p i i \sum_{i = 0}^n C_n^i p^ii ∑i=0n​Cni​pii。

然后我们有一个广为人知的组合恒等式: i C n i = n C n − 1 i − 1 iC_n^i = nC_{n - 1}^{i - 1} iCni​=nCn−1i−1​。

证明: i C n i = i ⋅ n ! i ! ( n − i ) ! = n ! ( i − 1 ) ! ( n − i ) ! = n ⋅ ( n − 1 ) ! ( i − 1 ) ! ( ( n − 1 ) − ( i − 1 ) ) ! = n C n − 1 i − 1 i C_n^i = i \cdot \frac{n!}{i!(n - i)!} = \frac{n!}{(i - 1)!(n - i)!} = n \cdot \frac{(n - 1)!}{(i - 1)!((n - 1) - (i - 1))!} = n C_{n - 1}^{i - 1} iCni​=i⋅i!(n−i)!n!​=(i−1)!(n−i)!n!​=n⋅(i−1)!((n−1)−(i−1))!(n−1)!​=nCn−1i−1​。

由于 i = 0 i = 0 i=0 时没有贡献,因此我们从 i = 1 i = 1 i=1 开始枚举。
∑ i = 1 n C n i p i i = n ∑ i = 1 n C n − 1 i − 1 p i = n ∑ i = 0 n − 1 C n − 1 i p i + 1 = n p ∑ i = 0 n − 1 C n − 1 i p i = n p ( p + 1 ) n − 1 \begin{aligned} &\sum_{i = 1}^n C_n^i p^ii \\ = &n\sum_{i = 1}^nC_{n - 1}^{i - 1} p^i \\ = &n \sum_{i = 0}^{n - 1} C_{n - 1}^i p^{i +1} \\ = &np\sum_{i = 0}^{n - 1}C_{n - 1}^i p^i \\ = & np(p + 1)^{n - 1} \end{aligned} ====​i=1∑n​Cni​piini=1∑n​Cn−1i−1​pini=0∑n−1​Cn−1i​pi+1npi=0∑n−1​Cn−1i​pinp(p+1)n−1​
算法到此就大功告成了。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>

#define MOD 998244353

using namespace std;

int n, p, k, ans;

inline int GetMOD(int k) { if(k >= MOD) k -= MOD; return k; }

inline int Pow(int k, int q)
{
	int res = 1;
	while(q)
	{
		if(q & 1) res = 1LL * res * k % MOD;
		k = 1LL * k * k % MOD;
		q >>= 1;
	}
	return res;
}

inline int Inv(int k) { return Pow(k, MOD - 2); }

int main()
{
	scanf("%d%d%d", &n, &p, &k);
	const int G = Pow(3, (MOD - 1) / k);
	int w = 1;
	for(register int t = 0; t < k; t++)
	{		
		if(w == 1) ans = GetMOD(ans + GetMOD(1LL * n * p % MOD * Pow(p + 1, n - 1) % MOD + Pow(p + 1, n)));
		else ans = GetMOD(ans + 1LL * GetMOD(1LL * w * Pow(GetMOD(1LL * p * w % MOD + 1), n) % MOD - Pow(p + 1, n) + MOD) * Inv(w - 1) % MOD);
		w = 1LL * w * G % MOD;
	}
	ans = 1LL * ans * Inv(k) % MOD;
	ans = GetMOD(ans - Pow(p + 1, n) + MOD);
	printf("%d\n", ans);
	return 0;
}

快速沃尔什变换

FWT ⁡ \operatorname{FWT} FWT,又称废物树,主要用于处理子集之间的关系。

用例题来讲:

给定两个长度为 2 n 2^n 2n 的数组 A , B A, B A,B,设:
C i = ∑ j ⊗ k = i A j ⋅ B k C_{i} = \sum_{j \otimes k = i} A_j\cdot B_k Ci​=j⊗k=i∑​Aj​⋅Bk​
求当 ⊗ \otimes ⊗ 为 or ⁡ , and ⁡ , xor ⁡ \operatorname{or}, \operatorname{and}, \operatorname{xor} or,and,xor 时的 C C C。 ( n ≤ 17 ) (n \le 17) (n≤17)

解法:

对于 or ⁡ \operatorname{or} or,我们考虑构造 F i ′ = ∑ j or ⁡ i = i F j {F}'_i = \sum_{j \operatorname{or} i = i} F_j Fi′​=∑jori=i​Fj​。

为什么要这样构造?

因为有:
A ′ × B ′ = ∑ j or ⁡ i = i A j ∑ k or ⁡ i = i B k = ∑ j or ⁡ i = i ∑ k or ⁡ i = i A j B k = ∑ ( j or ⁡ k ) or ⁡ i = i A j B k = C ′ \begin{aligned} A' \times B' &= \sum_{j \operatorname{or} i = i} A_j \sum_{k \operatorname{or} i = i} B_k \\ &= \sum_{j \operatorname{or} i = i} \sum_{k \operatorname{or} i = i} A_j B_k \\ &=\sum_{(j \operatorname{or} k) \operatorname{or} i = i} A_jB_k \\ &= C' \end{aligned} A′×B′​=jori=i∑​Aj​kori=i∑​Bk​=jori=i∑​kori=i∑​Aj​Bk​=(jork)ori=i∑​Aj​Bk​=C′​
现在我们考虑如何将 A → A ′ A \to A' A→A′。

其实就是一个类似 CDQ ⁡ \operatorname{CDQ} CDQ 分治的思想。

可以发现,在原数组的基础上,每一个数都会对自己二进制表示下的真超集造成贡献,而真超集的特点在于:至少存在一位,满足本身为 0 0 0 而超集为 1 1 1。

所以做法就很显然了:我们类似快速傅里叶变换的递归过程,从大到小枚举位数,把该位上为 0 0 0 的位置贡献到该位为 1 1 1 位置。

而逆变换怎么办呢?直接减回来呗。

可以发现两个变换可以合并:

inline void Or(int *f, int t = 1)
{
	for(register int len = 2; len <= n; len <<= 1)
	{
		int k = len >> 1;
		for(register int i = 0; i < n; i += len)
			for(register int j = 0; j < k; j++) 
				f[i + j + k] = GetMOD(f[i + j + k] + GetMOD(1LL * f[i + j] * t % MOD));
	}
	return;
}

与的构造和变换同理:

inline void And(int *f, int t = 1)
{
	for(register int len = 2; len <= n; len <<= 1)
	{
		int k = len >> 1;
		for(register int i = 0; i < n; i += len)
			for(register int j = 0; j < k; j++)
				f[i + j] = GetMOD(f[i + j] + GetMOD(1LL * f[i + j + k] * t % MOD));
	}
	return;
}

异或的变换就有点麻烦了,因为存在交叉贡献。

我们定义 a ⊗ b = popcount ⁡ ( a and ⁡ b )     m o d     2 a \otimes b = \operatorname{popcount}(a\operatorname{and} b)~\bmod~2 a⊗b=popcount(aandb) mod 2,其中 popcount ⁡ ( k ) \operatorname{popcount}(k) popcount(k) 表示 k k k 在二进制下 1 1 1 的个数。

那么我们存在一个性质: ( i ⊗ j ) xor ⁡ ( i ⊗ k ) = i ⊗ ( j xor ⁡ k ) (i \otimes j) \operatorname{xor} (i \otimes k) = i \otimes (j \operatorname{xor} k) (i⊗j)xor(i⊗k)=i⊗(jxork)。

证明的话,考虑化原式为 popcount ⁡ ( i and ⁡ j ) + popcount ⁡ ( i and ⁡ k ) ≡ popcnt ⁡ ( i and ⁡ ( j xor ⁡ k ) ) \operatorname{popcount}(i \operatorname{and} j) + \operatorname{popcount}(i \operatorname{and} k) \equiv \operatorname{popcnt}(i \operatorname{and} (j \operatorname{xor} k)) popcount(iandj)+popcount(iandk)≡popcnt(iand(jxork)),然后按位分讨即可,这里就不详细写了。

然后我们构造 F i ′ = ∑ i ⊗ j = 0 F j − ∑ i ⊗ j = 1 F j F'_i = \sum_{i \otimes j = 0}F_j- \sum_{i \otimes j = 1}F_j Fi′​=∑i⊗j=0​Fj​−∑i⊗j=1​Fj​。

那么有:
A ′ × B ′ = ( ∑ i ⊗ j = 0 A j − ∑ i ⊗ j = 1 A j ) ( ∑ i ⊗ k = 0 B k − ∑ i ⊗ k = 1 B k ) = ( ∑ i ⊗ j = 0 A j ) ( ∑ i ⊗ k = 0 B k ) − ( ∑ i ⊗ j = 0 A j ) ( ∑ i ⊗ k = 1 B k ) − ( ∑ i ⊗ j = 1 A j ) ( ∑ i ⊗ k = 0 B k ) + ( ∑ i ⊗ j = 1 A j ) ( ∑ i ⊗ k = 1 B k ) = ∑ i ⊗ ( j xor ⁡ k ) = 0 A j B k − ∑ i ⊗ ( j xor ⁡ k ) = 1 A j B k = C ′ \begin{aligned} A' \times B' &= (\sum_{i \otimes j = 0}A_j- \sum_{i \otimes j = 1}A_j)(\sum_{i \otimes k = 0}B_k- \sum_{i \otimes k = 1}B_k) \\ &= (\sum_{i \otimes j = 0}A_j)(\sum_{i \otimes k = 0}B_k) - (\sum_{i \otimes j = 0}A_j)(\sum_{i \otimes k = 1}B_k) - (\sum_{i \otimes j = 1}A_j)(\sum_{i \otimes k = 0}B_k) + (\sum_{i \otimes j = 1}A_j)(\sum_{i \otimes k = 1}B_k) \\ &=\sum_{i \otimes (j \operatorname{xor} k) = 0} A_jB_k - \sum_{i\otimes (j \operatorname{xor} k) = 1} A_jB_k \\ &= C' \end{aligned} A′×B′​=(i⊗j=0∑​Aj​−i⊗j=1∑​Aj​)(i⊗k=0∑​Bk​−i⊗k=1∑​Bk​)=(i⊗j=0∑​Aj​)(i⊗k=0∑​Bk​)−(i⊗j=0∑​Aj​)(i⊗k=1∑​Bk​)−(i⊗j=1∑​Aj​)(i⊗k=0∑​Bk​)+(i⊗j=1∑​Aj​)(i⊗k=1∑​Bk​)=i⊗(jxork)=0∑​Aj​Bk​−i⊗(jxork)=1∑​Aj​Bk​=C′​
现在的问题就在于计算贡献。

还是考虑从小到大枚举每一位 i i i。

我们设 F j ′ F'_j Fj′​ 为枚举到的位置 j j j 的 F ′ F' F′ 的值, F j 0 ′ F'_{j_0} Fj0​′​ 表示的是 j j j 的前 i − 1 i - 1 i−1 位和第 i i i 位的 0 0 0 组成的数的 F ′ F' F′ 值, F j 1 ′ F'_{j_1} Fj1​′​ 同理。(注意前面的 F j ′ F'_j Fj′​ 是要求的,后面的 F j 0 ′ , F j 1 ′ F'_{j_0},F'_{j_1} Fj0​′​,Fj1​′​ 是已经求出来的)

对于第 i i i 位本身就为 0 0 0 的位置,在 and ⁡ \operatorname{and} and 意义下这一位必然为 0 0 0,所以这个位置的贡献等于 j j j 在前 i − 1 i - 1 i−1 位所对应的两个位置的数值和,即 F j ′ = F j 0 ′ + F j 1 ′ F'_j = F'_{j_0} + F'_{j_1} Fj′​=Fj0​′​+Fj1​′​。

对于第 i i i 位为 1 1 1 的位置,显然 j j j 对应的第 i i i 位为 0 0 0 的位置会对其产生正贡献,第 i i i 位为 1 1 1 的位置会对其产生负贡献,所以 F j ′ = F j 0 ′ − F j 1 ′ F'_j = F'_{j_0} - F'_{j_1} Fj′​=Fj0​′​−Fj1​′​。

如果你学过小学奥数就可以发现就是在原来的基础上乘上了一个 1 2 \frac{1}{2} 21​。

inline void Xor(int *f, int t = 1)
{
	for(register int len = 2; len <= n; len <<= 1)
	{
		int k = len >> 1;
		for(register int i = 0; i < n; i += len)
			for(register int j = 0; j < (len >> 1); j++)
			{
				f[i + j] = GetMOD(f[i + j] + f[i + j + k]);
				f[i + j + k] = GetMOD(f[i + j] - f[i + j + k] * 2LL % MOD + MOD);
				f[i + j] = 1LL * f[i + j] * t % MOD, f[i + j + k] = 1LL * f[i + j + k] * t % MOD;
			}
	}
	return;
}

贴上完整代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>

#define MAXN 200005
#define MOD 998244353
#define Inv2 499122177

using namespace std;

int n, A[MAXN], B[MAXN], a[MAXN], b[MAXN];

inline int GetMOD(int k) { if(k >= MOD) k -= MOD; return k; }
inline void Copy() { return memcpy(a, A, sizeof A), memcpy(b, B, sizeof B), void(); }
inline void Multiply() { for(register int i = 0; i < n; i++) a[i] = 1LL * a[i] * b[i] % MOD; return; }
inline void Output() { for(register int i = 0; i < n; i++) printf("%d ", a[i]); return putchar('\n'), void(); }

inline void Or(int *f, int t = 1)
{
	for(register int len = 2; len <= n; len <<= 1)
	{
		int k = len >> 1;
		for(register int i = 0; i < n; i += len)
			for(register int j = 0; j < k; j++) 
				f[i + j + k] = GetMOD(f[i + j + k] + GetMOD(1LL * f[i + j] * t % MOD));
	}
	return;
}

inline void And(int *f, int t = 1)
{
	for(register int len = 2; len <= n; len <<= 1)
	{
		int k = len >> 1;
		for(register int i = 0; i < n; i += len)
			for(register int j = 0; j < k; j++)
				f[i + j] = GetMOD(f[i + j] + GetMOD(1LL * f[i + j + k] * t % MOD));
	}
	return;
}

inline void Xor(int *f, int t = 1)
{
	for(register int len = 2; len <= n; len <<= 1)
	{
		int k = len >> 1;
		for(register int i = 0; i < n; i += len)
			for(register int j = 0; j < (len >> 1); j++)
			{
				f[i + j] = GetMOD(f[i + j] + f[i + j + k]);
				f[i + j + k] = GetMOD(f[i + j] - f[i + j + k] * 2LL % MOD + MOD);
				f[i + j] = 1LL * f[i + j] * t % MOD, f[i + j + k] = 1LL * f[i + j + k] * t % MOD;
			}
	}
	return;
}

int main()
{
	scanf("%d", &n), n = (1 << n);
	for(register int i = 0; i < n; i++) scanf("%d", A + i);
	for(register int i = 0; i < n; i++) scanf("%d", B + i);
	Copy(), Or(a), Or(b), Multiply(), Or(a, MOD - 1), Output();
	Copy(), And(a), And(b), Multiply(), And(a, MOD - 1), Output();
	Copy(), Xor(a), Xor(b), Multiply(), Xor(a, Inv2), Output();
	return 0;
}

有了 FWT ⁡ \operatorname{FWT} FWT,我们还可以做一件事情——子集卷积:

给定序列 A A A 和 B B B,序列 C C C 满足:
C k = ∑ i and ⁡ j = 0 , i or ⁡ j = k A i B j C_k = \sum_{i \operatorname{and} j = 0, i \operatorname{or} j = k} A_i B_j Ck​=iandj=0,iorj=k∑​Ai​Bj​
求出 C C C。

题解:

对于限制 i or ⁡ j = k i \operatorname{or} j = k iorj=k,废物树可以直接干。

对于限制 i and ⁡ j = 0 i \operatorname{and} j = 0 iandj=0,我们发现可以转化为 ∣ i ∣ + ∣ j ∣ = ∣ i and ⁡ j ∣ |i| + |j| = |i \operatorname{and} j| ∣i∣+∣j∣=∣iandj∣。

所以,我们对每个集合大小跑一遍 FWT ⁡ \operatorname{FWT} FWT,然后计算的时候同时用两层循环枚举集合大小即可,最后再跑 IFWT ⁡ \operatorname{IFWT} IFWT。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>

#define FILE

#define Bitcnt(k) __builtin_popcount(k)

#define MAXN 2000005
#define MAXLOG 21
#define MOD 1000000009

using namespace std;

int n, m, a[MAXLOG][MAXN], b[MAXLOG][MAXN], c[MAXLOG][MAXN];

inline int GetMOD(int k) { if(k >= MOD) k -= MOD; return k; }

inline void FWT(int *f, int t = 1)
{
	for(register int len = 2; len <= m; len <<= 1)
	{
		int k = len >> 1;
		for(register int i = 0; i < m; i += len)
			for(register int j = 0; j < k; j++)
				f[i + j + k] = GetMOD(f[i + j + k] + 1LL * f[i + j] * t % MOD);
	}
	return;
}

int main()
{
	scanf("%d", &n), m = (1 << n);
	for(register int i = 0; i < m; i++) scanf("%d", &a[Bitcnt(i)][i]);
	for(register int i = 0; i < m; i++) scanf("%d", &b[Bitcnt(i)][i]);
	for(register int i = 0; i <= n; i++) FWT(a[i]), FWT(b[i]);
	for(register int i = 0; i <= n; i++)
		for(register int j = 0; j <= i; j++)
			for(register int k = 0; k < m; k++) c[i][k] = GetMOD(c[i][k] + 1LL * a[j][k] * b[i - j][k] % MOD);
	for(register int i = 0; i <= n; i++) FWT(c[i], MOD - 1);
	for(register int i = 0; i < m; i++) printf("%d ", c[Bitcnt(i)][i]);
	return 0;
}

子集反演

实际上,子集反演的前置知识就是上面提到的废物树和子集卷积。

它其实就是类似这样的一个式子:
F ( S ) = ∑ T ⊂ S G ( T ) ⇔ G ( S ) = ∑ T ⊂ S ( − 1 ) ∣ S − T ∣ F ( T ) F(S) = \sum_{T \subset S} G(T) \Leftrightarrow G(S) = \sum_{T \subset S} (-1)^{|S - T|} F(T) F(S)=T⊂S∑​G(T)⇔G(S)=T⊂S∑​(−1)∣S−T∣F(T)


例题

我们设 F ( S ) F(S) F(S) 为有多少种方案使得玩具的集合是 S S S 的子集,设 f ( S ) f(S) f(S) 为有多少个盒子的玩具集合是 S S S 的子集,那么显然有 F ( S ) = 2 f ( S ) − 1 F(S) = 2^{f(S)} - 1 F(S)=2f(S)−1(减不减 1 1 1 无所谓,反正最后会容斥掉)。再设 G ( S ) G(S) G(S) 为有多少种方案使得玩具集合恰好为 S S S,那么可以直接套用上面的子集反演公式。

(事实上,大多数计数题也用到了这个设置状态的思路,即设置 “至少”、“至多” 和 “恰好”。)

然后考虑求 f ( S ) f(S) f(S),可以发现这就是一个废物树的变换,直接套上即可。

(这里提一句, f ( S ) f(S) f(S) 实际上称作高维前缀和,而这种用 FWT ⁡ \operatorname{FWT} FWT 优化其的方法称做 s o s _ d p sos\_dp sos_dp,又叫求救优化。)

贴上代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>

#define MAXN 2000005
#define MAXM 21
#define MOD 1000000007

using namespace std;

int n, m, f[MAXN], F[MAXN], ans;

inline int GetMOD(int k) { if(k >= MOD) k -= MOD; return k; }
inline int Bitcnt(int k) { return __builtin_popcount(k); }

inline void FWT(int *f)
{
	for(register int len = 2; len <= (1 << m); len <<= 1)
	{
		int k = len >> 1;
		for(register int i = 0; i < (1 << m); i += len)
			for(register int j = 0; j < k; j++)
				f[i + j + k] = GetMOD(f[i + j + k] + f[i + j]);
	}
	return;
}

inline int Pow(int k, int p)
{
	int res = 1;
	while(p)
	{
		if(p & 1) res = 1LL * res * k % MOD;
		k = 1LL * k * k % MOD;
		p >>= 1;
	}
	return res;
}

int main()
{
	scanf("%d%d", &n, &m);
	for(register int i = 1; i <= n; i++)
	{
		int ki; scanf("%d", &ki);
		int st = 0;
		for(register int j = 1; j <= ki; j++)
		{
			int t; scanf("%d", &t);
			st |= (1 << (t - 1));
		}
		++f[st];
	}
	FWT(f);
	for(register int i = 0; i < (1 << m); i++) F[i] = Pow(2, f[i]) - 1;
	for(register int i = 0; i < (1 << m); i++)
	{
		if(Bitcnt(((1 << m) - 1) ^ i) & 1) ans = GetMOD(ans - F[i] + MOD);
		else ans = GetMOD(ans + F[i]);
	}
	printf("%d\n", ans);
	return 0;
}

T o   B e   C o n t i n u e d ⋯ ⋯ \mathit{To~Be~Continued\cdots\cdots} To Be Continued⋯⋯

LCD驱动芯片——BL55072A驱动程序 初始化子程序: START 0x7C; I2C子地址 0xEA;ICSET,软件复位芯片 0xC0;MODSET,关显示 0xF0;BLKCTL,关闪烁 0xA3;DISCTL,80Hz Line inversion,High Power mode 0xE8;ICSET,清复位bit1 STOP 显示刷新子程序: START 0x7C;I2C子地址 0xF0;BLKCTL,关闪烁 0xA3;DISCTL,80Hz Line inversion,High Power mode 0xE8或0xEC;ICSE 阅读详情

相关推荐

Abaqus 2023 保姆级安装避坑指南:从环境变量到许可证配置,一次搞定

本文提供Abaqus 2023的详细安装指南,涵盖环境变量设置、许可证配置等关键步骤,帮助用户避开常见安装陷阱。通过系统环境检查、目录结构规划、许可证服务器安装及Java环境配置等实用技巧,确保顺利完成安装并优化软件性能。

weixin_42524276的博客 425

反演变换

高中数学竞赛用过这种东西,但是大学一年没碰全忘了 …. emmmm…. 最近感觉大家都在用圆的反演变换 出 计算几何的 题 ,那就 …. 再学一遍吧反演变换基本概念 定义 : 对于平面上的一点 O 和一个非零常数 k ,使得平面上任何一个异于 O 点的 A 与 A’ 存在以下关系 : A O A‘三点共线; OA * OA’ = k ,则这个变换称为平面的一个反演变换,记做I(O,k)

Glory 的 博客 8349

在 Kubernetes 上部署 Label Studio

Label Studio 是一款开源的数据标注工具,支持多种标注任务,如文本标注、图像标注、音频标注等。

啥都会一点 668

圆的反演

原文链接: http://blog.youkuaiyun.com/acdreamers/article/details/16966369 反演的定义: 已知一圆C,圆心为O,半径为r,如果P与P’在过圆心O的直线上,且,则称P与P'关于O互为反演。 反演的性质: (1)除反演中心外,平面上的每一个点都只有唯一的反演点,且这种关系是对称的,位于反演圆上

呼,是出一口气;吸,是争一口气! 4196

论文阅读:Visual Relationship Detection with Language Priors

Visual Relationship Detection with Language Priors(ECCV2016) 文章   尽管大多数的relationship并不常见,但是它们的object和predicate却更频繁地独立出现。paper用这个insight分别独立训练训练object和predicate的模型,然后再进行组合来预测relationship。Visual relatio...

Kivee的博客 2877

期权专题9:雪球期权(一)(BSM+蒙特卡罗定价)

python;雪球定价;蒙特卡罗

mhmh123789的博客 8101

圆的反演变换

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4773 题意:给定两个圆,告诉半径和圆心,它们是相离的,在这两个圆外给定一个点p,求符合条件:过点p的圆且与已知的两个 圆外切的所有圆的总数和它们的圆心坐标和半径。 分析:根据题意,我们设已知两个圆的半径分别为和,它们的圆心分别为和,设点p的坐标为 并设要求的圆的圆心

ACdreamer 2万+

Transformer在量化交易中的应用:从时序预测到策略生成

Transformer模型凭借其强大的自注意力机制,在自然语言处理领域取得了革命性突破。其核心原理是通过并行计算和动态权重分配,有效捕捉序列数据中的长距离依赖关系。这一特性使其在金融时间序列分析中展现出巨大技术价值,能够处理市场数据中的复杂非线性模式和长期关联。在量化交易场景下,Transformer可将价格、成交量等连续数据通过离散化分桶处理,转化为类似文本的序列,进而进行预测或直接生成交易信号。本文以QuantGPT项目为例,探讨了如何将这一前沿AI架构迁移应用于金融领域,涵盖了从数据作为“语言”的编码

weixin_26757949的博客 363

圆的反演变换(HDU4773)

题意:给出两个相离的圆O1,O2和圆外一点P,求构造这样的圆:同时与两个圆相外切,且经过点P,输出圆的圆心和半径 分析:画图很容易看出这样的圆要么存在一个,要么存在两个:此题直接解方程是不容易的,先看看反演的定义:已知一圆C,圆心为O,半径为r,如果P与P’在过圆心O的直线上,且,则称P与P'关于O互为反演。 反演的性质: 首先设出反演圆心O和反演半径R 1、圆外一点P与圆内一...

weixin_30567471的博客 313

IM 系统设计:仿微信架构,Netty + Protobuf + WebSocket 如何支撑百万级长连接?

相比 JSON,Protobuf 的体积通常只有前者的 1/5 到 1/10,且解析速度快一个数量级。// 消息类型:1-登录,2-心跳,3-单聊,4-群聊,5-ACK// 发送方ID// 接收方ID// 消息内容// 时间戳// 消息唯一ID,用于去重和ACK。

BUG猿的博客 1378

计算机视觉卷积神经网络(CNN)基础:从LeNet到ResNet

计算机视觉卷积神经网络(CNN)基础:从LeNet到ResNet,在计算机视觉领域,卷积神经网络(Convolutional Neural Network,CNN)已然成为推动众多任务发展的核心力量。从早期简单的图像识别,到如今复杂的目标检测、语义分割、图像生成等任务,CNN 都展现出了卓越的性能。CNN 的发展历程见证了一系列经典模型的诞生,其中 LeNet 作为开山之作,为 CNN 的发展奠定了基础,而 ResNet 则通过创新性的设计,突破了传统神经网络的限制,使得网络能够构建得更深、性能更优。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 14万+

03_拉氏反变换&&传递函数

反演公式 f(t)=12πj∫σ−j∞σ+j∞F(s)⋅etsdsf(t)=\frac{1}{2\pi j}\int_{\sigma-j\infty}^{\sigma+j\infty}F(s)·e^{ts}dsf(t)=2πj1​∫σ−j∞σ+j∞​F(s)⋅etsds查表法(分解部分分式法) {试凑法系数比较法留数法\begin{cases}试凑法\\系数比较法\\留数法\end{cases}⎩⎨⎧​试凑法系数比较法留数法​模态: 如果n阶微分方程的特征根是λ1,λ2,...λn\lambda_1

nanxl1的博客 2万+

药物对接仿真软件:HADDOCK_(5).HADDOCK中蛋白质-配体对接流程

半柔性能量最小化阶段是在刚体能量最小化的基础上,允许配体的部分柔性,通过优化配体的构象来进一步提高对接的准确性。半柔性能量最小化阶段是在刚体能量最小化的基础上,允许配体的部分柔性,通过优化配体的构象来进一步提高对接的准确性。如果是PDB格式,确保文件包含所有必要的原子信息。对接完成后,HADDOCK会生成一系列的输出文件,包括对接后的蛋白质-配体复合物结构文件、能量评分文件和统计分析文件。对接完成后,HADDOCK会生成一系列的输出文件,包括对接后的蛋白质-配体复合物结构文件、能量评分文件和统计分析文件。

kkchenjj的博客 1080

深入详解python高级特性——函数柯里化(Currying)与反柯里化

前言:本章的内容本来很简单,但是涉及到的理论部分相对较多,想要彻底弄懂前因后果需要具备以下几个知识点, (1)python的高阶函数 (2)python的装饰器本质 (3)Python的functools模块里面的偏函数的本质 这三块类容我在之前的文章中已经有详细说明了,不再赘述,可以参考下面的连接中的文章: 博客专栏分类,关于Python的各种高级特性都有说明 关于functoo...

MIss-Y的博客 7748

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Java中lambda表达式的演变过程

通过一个小例子来看看lambda表达式的演变方式,可以看出来代码影响范围逐渐变小,最终代码量也逐渐变少。 package lambda; /** * lambda表达式的演变 * 使用最后一种方式的前提是方法中之后一个函数 */ public class LambdaEx1 { //2、静态内部类 static class Like2 implements ILike { @Override public void like() { .

爱小七爱生活的博客 313

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Java深度解析从Lambda表达式到响应式编程的范式演变

与传统的同步阻塞式编程不同,响应式编程采用非阻塞、异步的方式处理数据流,能够更高效地利用系统资源,特别适合处理高并发、实时性要求高的场景。同时,响应式编程正在向更广泛的领域扩展,如响应式数据库访问、响应式消息传递等,形成完整的响应式系统架构。更重要的是,这种范式转变促使开发者从传统的命令式思维转向声明式思维,更加关注数据流和变换关系,而非具体的执行步骤。此外,函数式编程的不变性和纯函数特性也有助于提高代码的可测试性和可维护性,减少了多线程环境下的竞态条件和副作用问题,为构建稳健的并发系统提供了有力保障。

builder_ming的博客 904

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Lambda表达式的简介和演变

Lambda表达式 函数式接口 任何接口,如果只包含唯一一个抽象方法,就是一个函数式接口,例: Runnable() @FunctionalInterface public interface Runnable { public abstract void run(); } 函数式接口可用Lambda表达式 接口: public interface ILike { void lambda(); } 普通类: public class Like impleme

code_mzh的博客 400

Lambda表达式演变过程

Lanbda表达式演变过程 lanbda表达式使用条件 使用lamda表达式前提:是函数式接口 函数式接口:任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。 无参数时 使用普通类来实现接口 使用静态内部类 使用局部内部类 使用匿名内部类 使用普通lambda简化 使用无花括号lambda简化 注意: - 花括号只能在只有一条语句时才可以省略 - 参数小括号不能省略 只有一个参数时

Coding~Farmer 327

Lambda表达式演变

Lambda表达式是一种匿名函数。 演变步骤: 一般的方法委托 => 匿名函数委托 => Lambda表达式 Lambda表达式其实并不陌生,他的前生就是匿名函数,所以要谈Lambda表达式,就不得不谈匿名函数,要谈匿名函数,那又要不得不谈委托。 委托非常好理解,类似于C++里面的函数指针(指向了一个方法),并且委托约束了待...

denglei011124的博客 179

lambda表达式(内部类演变)随笔

lambda表达式(内部类演变)随笔 一、简介 Lambda表达式,也可称为闭包,Java8发布的; Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中); 使用Lambda表达式可以使代码变得更加简洁紧凑。 二、语法、特征 1.语法 (parameters) -> expression 或 (parameters) -> {statements;} 2.特征 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需

诺_404 的博客 201

Lamda表达式好处、Lamda表达式演变、Lamda表达式使用总结

Lamda表达式好处、演变、使用总结使用Lamda表达式的好处Functional Interfere:函数式接口Lamda表达式的演变过程1.一个接口对应多个实现类,造成编写的文件数量很多2.使用内部类定义实现类2.1静态内部类2.2局部内部类2.3匿名内部类3.Lamda表达式3.1Lamda表达式继续简化Lamda表达式使用总结 使用Lamda表达式的好处 避免匿名内部类定义过多 可以让代码看起来更简洁 去掉一对无意义的代码,只留下核心逻辑 Functional Interfere:函数式接口 理

郝开的博客 2083

【函数式编程实战】(一)Java演变与函数式编程

Java8函数式编程实战开篇暴击,电商多重优惠核销代码初探

小明的Java问道之路 3785

Lambda表达式详解及演变过程

Lambda表达式 λ希腊字母表中排序第十一位的字母,英语名称为:Lambda 作用: 避免内部类定义过多 可以让代码看起来简洁 去掉一些没有意义的代码,只留下核心代码 函数式接口:本质上就是一个接口,但是接口内部只有一个抽象方法,这种借口就叫做函数式接口。 下面演示lambda表达式的演变↓↓↓↓↓↓↓ //以前我们调用一个接口实现类中的方法的过程是这样的 //三个文件,一个接口文件,一个实现类文件,一个主程序文件 public interface SelfIntroduction{ voi

koala的博客 294

函数式编程&Lambda表达式

lambda

weixin_53085985的博客 1024

技术图文:匿名方法是怎样演变为Lambda表达试的?

背景 “Lambda 表达式”(lambda expression)是一个匿名函数,Lambda 表达式基于数学中的 λ演算得名,直接对应于其中的 lambda 抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda 表达式可以表示闭包(注意和数学传统意义上的不同)。 技术分析 下面代码演示怎么从一个匿名函数(方法) 推导出一个Lambda表达式。 Ste...

老马的程序人生 3399

Week 1 Day 3 :Lambda表达式与函数对象设计

Lambda表达式是现代C++函数式编程的基石,其简洁语法背后是复杂的类型系统和编译器优化。虽然今日理论主题是Lambda表达式,但今日的实战练习是实现shared_ptr的引用计数机制。Lambda表达式是现代C++函数式编程的核心特性,它通过简洁的语法实现了匿名函数的定义与传递。:Lambda的本质是编译器生成的匿名类,捕获列表对应类的成员变量,函数体对应类的operator()实现。Lambda不仅改变了C++的编码风格,更是现代软件设计思想(如响应式编程、数据流处理)在语言层面的体现。

phaethonWB的博客 1242
上一篇: 积性函数的筛法
下一篇: 整除和同余相关理论
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值