[LOJ 6055]「from CommonAnts」一道数学题 加强版(神仙拉格朗日插值) | 错题本

文章目录

题目

「from CommonAnts」一道数学题 加强版

分析

F k ( n ) = f ( k , n ) F_k(n) = f(k, n) ,统计每个 x k x^k 的贡献可以得到 F k ( n ) = i = 0 n 1 ( 2 n i 1 i k ) + n k F_k(n) = \sum_{i = 0}^{n - 1} \left(2^{n - i - 1} \cdot i^k\right) + n^k 具体来说可以画个二叉树理解:树上深度为 i i 的结点的权值为 ( n i + 1 ) k (n - i + 1)^k ,根以及他的右子树权值和为 F k ( n ) F_k(n) ,根的左儿子和根的左儿子的右子树权值和 F k ( n 1 ) F_k(n - 1) ,以此类推。

显然不能直接插,因为 n n 在指数上。化简一下:
F k ( n ) = i = 0 n 1 ( 2 n i 1 i k ) + n k = 2 n 1 i = 0 n 1 i k 2 i + n k \begin{aligned} F_k(n) &= \sum_{i = 0}^{n - 1} \left(2^{n - i - 1} \cdot i^k\right) + n^k \\ &= 2^{n - 1}\sum_{i = 0}^{n - 1} \frac{i^k}{2^i} + n^k \end{aligned} a = 1 2 a = \frac{1}{2} ,则 F k ( n ) = 2 n 1 i = 0 n 1 a i i k + n k F_k(n) = 2^{n - 1}\sum_{i = 0}^{n - 1} a^ii^k + n^k 指数转下降幂可以方便错位相减找递推式,进而找到指数函数的多项式形式: F k ( n ) = 2 n 1 i = 0 n 1 ( a i j = 0 k { k j } i j ) + n k = 2 n 1 j = 0 k { k j } i = 0 n 1 a i i j + n k \begin{aligned} F_k(n) &= 2^{n - 1}\sum_{i = 0}^{n - 1}\left(a^i \sum_{j = 0}^k {k \brace j} i^{\underline j}\right) + n^k \\ &= 2^{n - 1} \sum_{j = 0}^k {k \brace j} \sum_{i = 0}^{n - 1} a^ii^{\underline j} + n^k \end{aligned} S k ( n ) = i = 0 n 1 a i i k S_k(n) = \sum_{i = 0}^{n - 1} a^ii^{\underline k} ,则 F k ( n ) = 2 n 1 j = 0 k { k j } S j ( n ) + n k F_k(n) = 2^{n - 1} \sum_{j = 0}^k {k \brace j} S_j(n) + n^k

指数转下降幂:
{ n m } {n \brace m} 表示第二类斯特林数,即“ n n 个不同的球,放入 m m 个相同的盒子,盒子不可以为空的方案数”。有公式 n k = i = 0 k { k i } i ! C n i n^k = \sum_{i = 0}^{k} {k \brace i} \cdot i! \cdot C_n^i 等式左边是“ k k 个不同的球放入 n n 个不同的盒子,盒子可以为空的方案数”,右边即为枚举不为空的盒子数,分别计算,因此两边相等。
把组合数拆开可以得到: n k = i = 0 k { k i } n i n^k = \sum_{i = 0}^{k} {k \brace i} \cdot n^{\underline i}

考虑求 S k ( n ) S_k(n) 的递推式,用错位相减法:
S k ( n ) = i = 0 n 1 a i i k a S k ( n ) = i = 1 n a i ( i 1 ) k ( 1 a ) S k ( n ) = i = 1 n 1 a i [ i k ( i 1 ) k ] + a 0 0 k a n ( n 1 ) k = i = 1 n a i [ i k ( i 1 ) k ] a n n k + a n ( n 1 ) k + a 0 0 k a n ( n 1 ) k = i = 1 n a i [ i k ( i 1 ) k ] a n n k + a 0 0 k = i = 1 n a i ( i 1 ) k 1 [ i ( i k ) ] a n n k + a 0 0 k = a k i = 0 n 1 a i i k 1 a n n k + a 0 0 k = a k S k 1 ( n ) a n n k + a 0 0 k S k ( n ) = a k 1 a S k 1 ( n ) a n n k + a 0 0 k 1 a \begin{aligned} S_k(n) &= \sum_{i = 0}^{n - 1} a^ii^{\underline k} \\ aS_k(n) &= \sum_{i = 1}^{n} a^i(i - 1)^{\underline k} \\ \Rightarrow (1 - a)S_k(n) &= \sum_{i = 1}^{n - 1} a^i \left[i^{\underline k} - (i - 1)^{\underline k}\right] + a^00^{\underline k} - a^n(n - 1)^{\underline k} \\ &= \sum_{i = 1}^n a^i \left[i^{\underline k} - (i - 1)^{\underline k}\right] - a^nn^{\underline k} + a^n(n - 1)^k + a^00^{\underline k} - a^n(n - 1)^k \\ &= \sum_{i = 1}^n a^i \left[i^{\underline k} - (i - 1)^{\underline k}\right] - a^nn^{\underline k} + a^00^{\underline k} \\ &= \sum_{i = 1}^n a^i (i - 1)^{\underline{k - 1}}[i - (i - k)] - a^nn^{\underline k} + a^00^{\underline k} \\ &= ak\sum_{i = 0}^{n - 1} a^i i^{\underline{k - 1}} - a^nn^{\underline k} + a^00^{\underline k} \\ &= akS_{k - 1}(n) - a^nn^{\underline k} + a^00^{\underline k} \\ \Rightarrow S_k(n) &= \frac{ak}{1 - a} S_{k - 1}(n) - \frac{a^nn^{\underline k} + a^00^{\underline k}}{1 - a} \end{aligned} 特别地, S 0 ( n ) = i = 0 n 1 a i = 1 a n 1 a S_0(n) = \sum_{i = 0}^{n - 1} a^i = \frac{1 - a^n}{1 - a} 。将 a a 替换为 1 2 \frac{1}{2} 得到 S k ( n ) = { 2 ( 1 1 2 n ) k = 1 k S k 1 ( n ) n k 2 n 1 k > 1 S_k(n) = \begin{cases} 2\left(1 - \dfrac{1}{2^n}\right) & k = 1 \\ k S_{k - 1}(n) - \dfrac{n^{\underline k}}{2^{n - 1}} & k > 1 \end{cases} 这时候手刨几项:

  • S 0 ( n ) = 2 ( 1 1 2 n ) S_0(n) = 2(1 - \frac{1}{2^n})
  • S 1 ( n ) = 1 2 ( 1 1 2 n ) n 2 n 1 S_1(n) = 1 \cdot 2(1 - \frac{1}{2^n}) - \frac{n}{2^{n - 1}}
  • S 2 ( n ) = 2 [ 1 2 ( 1 1 2 n ) n 2 n 1 ] n ( n 1 ) 2 n S_2(n) = 2\left[1 \cdot 2(1 - \frac{1}{2^n}) - \frac{n}{2^{n - 1}}\right] - \frac{n(n - 1)}{2^n}
  • S 3 ( n ) = 3 { 2 [ 1 2 ( 1 1 2 n ) n 2 n 1 ] n ( n 1 ) 2 n } n ( n 1 ) ( n 2 ) 2 n S_3(n) = 3 \left\{2\left[1 \cdot 2(1 - \frac{1}{2^n}) - \frac{n}{2^{n - 1}}\right] - \frac{n(n - 1)}{2^n}\right\} - \frac{n(n - 1)(n - 2)}{2^n}

接下来就是神仙操作了,还记得常系数齐次线性递推么? 将常数项分成两部分,将含 n n 的部分提出一个 1 2 n \frac{1}{2^n} S 3 ( n ) = 3 { 2 [ 1 2 ( 1 1 2 n ) n 2 n 1 ] n ( n 1 ) 2 n } n ( n 1 ) ( n 2 ) 2 n = 12 1 2 n [ 12 + 6 n + 3 n ( n 1 ) + n ( n 1 ) ( n 2 ) ] \begin{aligned} S_3(n) &= 3 \left\{2\left[1 \cdot 2(1 - \frac{1}{2^n}) - \frac{n}{2^{n - 1}}\right] - \frac{n(n - 1)}{2^n}\right\} - \frac{n(n - 1)(n - 2)}{2^n} \\ &= 12 - \frac{1}{2^n}[12 + 6n + 3n(n - 1) + n(n - 1)(n- 2)] \end{aligned} 两个 12 12 是一样的!其实原因就在于 S 0 ( n ) = 2 ( 1 1 2 n ) S_0(n) = 2(1 - \frac{1}{2^n}) ,两个 1 1 可以分给两边,使得新的一个 S k ( n ) S_k(n) 可以写成 S k ( n ) = G k ( 0 ) G k ( n ) 2 n S_k(n) = G_k(0) - \frac{G_k(n)}{2^n} 其中 G k ( n ) G_k(n) 显然是一个 k k 次多项式,而 G k ( 0 ) G_k(0) 正式它的常数项!

可能的疑问是,为什么不能直接提出一个 1 2 n \frac{1}{2^n} ,变成 S k ( n ) = T k ( n ) 2 n S_k(n) = \frac{T_k(n)}{2^n} 原因是, S 0 ( n ) S_0(n) 事实上是两部分,如果不分开的话, T k ( n ) T_k(n) 中会有 2 n 2^n 这样的东西( S 0 ( n ) S_0(n) 中的 “ 1 1 ”造成的),所以不可以直接提 1 2 n \frac{1}{2^n} 。分开过后, G k ( n ) G_k(n) 就不含指数函数了!

回代进 F k ( n ) F_k(n) F k ( n ) = 2 n 1 j = 0 k { k j } S j ( n ) + n k = 2 n 1 j = 0 k { k j } ( G j ( 0 ) G j ( n ) 2 n ) + n k = 2 n 1 ( i = 0 k { k i } G i ( 0 ) i = 0 k { k i } G i ( n ) 2 n ) + n k \begin{aligned} F_k(n) &= 2^{n - 1} \sum_{j = 0}^k {k \brace j} S_j(n) + n^k \\ &= 2^{n - 1} \sum_{j = 0}^k {k \brace j} \left(G_j(0) - \frac{G_j(n)}{2^n}\right) + n^k \\ &= 2^{n - 1} \left(\sum_{i = 0}^k {k \brace i} G_i(0) - \frac{\sum_{i = 0}^k {k \brace i} G_i(n)}{2^n}\right) + n^k \end{aligned} H k ( n ) = i = 0 k { k i } G i ( n ) H_k(n) = \sum_{i = 0}^k {k \brace i} G_i(n) 显然 H i ( n ) H_i(n) k k 次多项式,且 F k ( n ) = 2 n 1 ( H k ( 0 ) H k ( n ) 2 n ) + n k F_k(n) = 2^{n - 1}\left(H_k(0) - \frac{H_k(n)}{2^n}\right) + n^k 接下来求出 R k ( n ) = H k ( 0 ) 1 2 n H k ( n ) R_k(n) = H_k(0) - \frac{1}{2^n}H_k(n) 即可。

  • H k ( 0 ) H_k(0) 的部分:
    这个函数我们一项都算不出来,回顾一下开头,发现 R k ( n ) R_k(n) 可以算: R k ( n ) = i = 0 n 1 i k 2 i R_k(n) = \sum_{i = 0}^{n - 1} \frac{i^k}{2^i} 把式子弄一下 H k ( n ) = 2 n H k ( 0 ) 2 n R k ( n ) H_k(n) = 2^nH_k(0) - 2^nR_k(n) 那么用一个二元组 { x , y } \{x, y\} 表示 H k ( n ) H_k(n) H k ( n ) = x H k ( 0 ) y H_k(n) = xH_k(0) - y ,这个二元组显然可以快速地进行加、减和乘一个常的运算。有了这些我们就可以用 H k ( 0 ) , H k ( 1 ) , , H k ( k ) H_k(0), H_k(1), \cdots, H_k(k) 的二元表示插出 H k ( k + 1 ) H_k(k + 1) 的二元表示记为 { a 1 , b 1 } \{a_1,b_1\} H k ( n ) = 2 n H k ( 0 ) 2 n R k ( n ) H_k(n) = 2^nH_k(0) - 2^nR_k(n) 也能得到一个 H k ( k + 1 ) H_k(k + 1) 的二元表示记为 { a 2 , b 2 } \{a_2, b_2\} 。由 H k ( k + 1 ) = H k ( k + 1 ) H_k(k + 1) = H_k(k + 1) 得到 a 1 H k ( 0 ) b 1 = a 2 H k ( 0 ) b 2 a_1H_k(0) - b_1 = a_2H_k(0) - b_2 于是 H k ( 0 ) = b 1 b 2 a 1 a 2 H_k(0) = \frac{b_1 - b_2}{a_1 - a_2}

    注意这两个二元表示是不等价的!可以理解为只要给出次数,拉格朗日插值就可以形式化地创造一个表示方法,这个新的值的计算方式与你原来计算点值的方式没有任何关系,因此这个方程有解。

  • H k ( n ) H_k(n) 的部分:
    得到 H k ( 0 ) H_k(0) ,就知道了 H k ( 1 ) , H k ( 2 ) , , H k ( k ) H_k(1), H_k(2), \cdots, H_k(k) ,直接拉格朗日插值即可。


还有一个细节是算 2 n 2^n 什么的, n n 有一百万位显然不能快速幂……要用欧拉定理: a φ ( p ) 1   ( mod  p ) a^{\varphi(p)} \equiv 1 \ (\text{mod} \ p) 这里 p = 1 0 9 + 7 p = 10^9 + 7 是个质数,所以 φ ( p ) = p 1 \varphi(p) = p - 1 ,所以 2 n 2 n  mod  ( p 1 )   ( mod  p ) 2^n \equiv 2^{n \ \text{mod} \ (p - 1)} \ (\text{mod} \ p)


做完了。这题太夸张了。据说 高阶差分 可以简单一点点,或者最开始就往 常系数线性递推 化。太菜了看不怎么懂……

代码

变量名有出入,但思路是完全一致的。

#include <algorithm>
#include <cstdio>
#include <cstring>

typedef std::pair<int, int> PII;

const int MAXN = 1000000;
const int MOD = 1000000007;
const int PHI = MOD - 1;

inline int Add(int x, const int &y) {
	x += y; if (x >= MOD) x -= MOD; return x;
}

inline int Sub(int x, const int &y) {
	x -= y; if (x < 0) x += MOD; return x;
}

inline int Mul(const int &x, const int &y) {
	return (long long)x * y % MOD;
}

int Pow(int x, int y) {
	int ret = 1;
	while (y) {
		if (y & 1)
			ret = Mul(ret, x);
		y >>= 1;
		x = Mul(x, x);
	}
	return ret;
}

const int INV2 = Pow(2, MOD - 2);

PII operator + (const PII &x, const PII &y) {
	return std::make_pair(Add(x.first, y.first), Add(x.second, y.second));
}

PII operator - (const PII &x, const PII &y) {
	return std::make_pair(Sub(x.first, y.first), Sub(x.second, y.second));
}

PII operator * (const PII &x, const int &y) {
	return std::make_pair(Mul(x.first, y), Mul(x.second, y));
}

int N, P, K;
int Fac[MAXN + 5], InvFac[MAXN + 5];

int H[MAXN + 5];
int F[MAXN + 5];
PII G[MAXN + 5];

int Pre[MAXN + 5], Suf[MAXN + 5];

void Init(int n) {
	Pre[0] = n;
	for (int i = 1; i <= K; i++)
		Pre[i] = Mul(Pre[i - 1], Sub(n, i));
	Suf[K + 1] = 1;
	for (int i = K; i >= 0; i--)
		Suf[i] = Mul(Suf[i + 1], Sub(n, i));
}

PII Lagrange1(int n) {
	Init(n);
	PII res(0, 0);
	for (int i = 0; i <= K; i++) {
		int A = Mul(i ? Pre[i - 1] : 1, Suf[i + 1]);
		int B = Mul(InvFac[i], InvFac[K - i]);
		PII C = G[i] * Mul(A, B);
		res = ((K - i) & 1) ? (res - C) : (res + C);
//		printf("%d %d (%d, %d)\n", A, B, res.first, res.second);
	}
	return res;
}

int Lagrange2(int n) {
	Init(n);
	int res = 0;
	for (int i = 0; i <= K; i++) {
		int A = Mul(i ? Pre[i - 1] : 1, Suf[i + 1]);
		int B = Mul(InvFac[i], InvFac[K - i]);
		int C = Mul(H[i], Mul(A, B));
		res = ((K - i) & 1) ? Sub(res, C) : Add(res, C);
	}
	return res;
}

int main() {
	char c = getchar();
	while (c < '0' || c > '9')
		c = getchar();
	while (c >= '0' && c <= '9') {
		P = ((long long)P * 10 + c - '0') % PHI;
		N = ((long long)N * 10 + c - '0') % MOD;
		c = getchar();
	}
	scanf("%d", &K);

	Fac[0] = 1;
	for (int i = 1; i <= K; i++)
		Fac[i] = Mul(Fac[i - 1], i);
	InvFac[K] = Pow(Fac[K], MOD - 2);
	for (int i = K - 1; i >= 0; i--)
		InvFac[i] = Mul(InvFac[i + 1], i + 1);

	for (int i = 1; i <= K + 1; i++)
		F[i] = Add(F[i - 1], Mul(Pow(INV2, i - 1), Pow(i - 1, K)));
	for (int i = 0; i <= K + 1; i++)
		G[i] = std::make_pair(Pow(2, i), Mul(Pow(2, i), F[i]));

	PII x = G[K + 1], y = Lagrange1(K + 1);
	int A = Sub(y.second, x.second);
	int B = Sub(x.first, y.first);
	H[0] = Mul(A, Pow(B, MOD - 2));
	for (int i = 1; i <= K; i++)
		H[i] = Add(Mul(G[i].first, H[0]), G[i].second);

	int Ans = Sub(Mul(Lagrange2(N), Pow(INV2, P)), H[0]);
	Ans = Add(Mul(Pow(2, P ? (P - 1) : (MOD - 2)), Ans), Pow(N, K));
	printf("%d", Ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/C20190102/article/details/107474873
今日推荐