zl程序教程

您现在的位置是:首页 >  其它

当前栏目

【ybt金牌导航8-3-4】【luogu CF622F】K次方求和 / The Sum of the k-th Powers(拉格朗日插值)

The of 导航 sum Luogu 求和 ybt 金牌
2023-09-27 14:28:28 时间

K次方求和 / The Sum of the k-th Powers

题目链接:ybt金牌导航8-3-4 / luogu CF622F

题目大意

给你 n,k,要你求 i=1~n 每个 i^k 的和。

思路

首先重新给式子:
∑ i = 1 n i k \sum\limits_{i=1}^ni^k i=1nik

然后这个东西你设成 f ( n ) f(n) f(n),它其实是一个 k + 1 k+1 k+1 次的多项式。

这里简单的证明一下:
你可以通过差分它得到 i k i^k ik,然后接着每次差分就依次变成:
i k − 1 , i k − 2 , . . . i^{k-1},i^{k-2},... ik1,ik2,...

最后会变成 i 0 = 1 i^0=1 i0=1,即全部都是 1 1 1 的数组。(这个是 0 0 0 次的)
所以反过来就是前缀和,每次次数加一,加到 i k i^k ik k k k 次,再前缀就是 k + 1 k+1 k+1 次了。

然后你就直接用拉格朗日插值,你任意取值就直接取 1 ∼ k + 2 1\sim k+2 1k+2 的值,然后用连续的那种搞可以了。

代码

#include<cstdio>
#define ll long long
#define mo 1000000007

using namespace std;

ll n, k, y[1000003], jc[1000003];
ll ans, now, pre[1000003], suf[1000003];

ll ksm(ll x, ll y) {
	ll re = 1;
	while (y) {
		if (y & 1) re = re * x % mo;
		x = x * x % mo;
		y >>= 1;
	}
	return re;
}

ll work(int k, int n) {
	pre[0] = 1;
	for (int i = 1; i <= n; i++)
		pre[i] = pre[i - 1] * (k - i + mo) % mo;
	suf[n + 1] = 1;
	for (int i = n; i >= 1; i--)
		suf[i] = suf[i + 1] * (k - i + mo) % mo;
	jc[0] = 1;
	for (int i = 1; i <= n; i++) jc[i] = jc[i - 1] * i % mo;
	
	ll ans = 0;
	for (int i = 1; i <= n; i++) {
		ans = (ans + y[i] * pre[i - 1] % mo * suf[i + 1] % mo * ksm(jc[i - 1] * jc[n - i] % mo * (((n - i) & 1) ? mo - 1 : 1) % mo, mo - 2) % mo) % mo;
	}
	return ans;
}

int main() {
	scanf("%lld %lld", &n, &k);
	for (int i = 1; i <= k + 2; i++) {
		now = (now + ksm(i, k)) % mo;
		y[i] = now;
	}
	
	printf("%lld", work(n, k + 2));
	
	return 0;
}